diff --git a/bp-xprofile/bp-xprofile-activity.php b/bp-xprofile/bp-xprofile-activity.php
index 01ffcc3..3c53ff0 100644
--- a/bp-xprofile/bp-xprofile-activity.php
+++ b/bp-xprofile/bp-xprofile-activity.php
@@ -132,3 +132,80 @@ function bp_xprofile_new_avatar_activity() {
 	) );
 }
 add_action( 'xprofile_avatar_uploaded', 'bp_xprofile_new_avatar_activity' );
+
+/**
+ * Add an activity item when a user has updated his profile.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param int $user_id ID of the user who has updated his profile.
+ * @param array $field_ids IDs of the fields submitted.
+ * @param bool $errors True if validation or saving errors occurred, otherwise
+ *        false.
+ * @return bool True on success, false on failure.
+ */
+function bp_xprofile_updated_profile_activity( $user_id, $field_ids, $errors ) {
+	// If there were errors, don't post
+	if ( ! empty( $errors ) ) {
+		return false;
+	}
+
+	// Throttle to one activity of this type per 2 hours
+	$existing = bp_activity_get( array(
+		'max' => 1,
+		'filter' => array(
+			'user_id' => $user_id,
+			'object' => buddypress()->profile->id,
+			'action' => 'updated_profile',
+		),
+	) );
+
+	if ( empty( $existing['activities'] ) ) {
+		$throttle = false;
+	} else {
+		// Default throttle time is 2 hours. Filter to change (in seconds)
+		$throttle_period = apply_filters( 'bp_xprofile_updated_profile_activity_throttle_time', 60*60*2 );
+		$then = strtotime( $existing['activities'][0]->date_recorded );
+		$now  = strtotime( bp_core_current_time() );
+
+		$throttle = ( $now - $then ) < $throttle_period;
+	}
+
+	if ( $throttle ) {
+		return false;
+	}
+
+	// If we've reached this point, assemble and post the activity item
+
+	// Note for translators: The natural phrasing in English, "Joe updated
+	// his profile", requires that we know Joe's gender, which we don't. If
+	// your language doesn't have this restriction, feel free to use a more
+	// natural translation.
+	$profile_link = trailingslashit( bp_core_get_user_domain( $user_id ) . buddypress()->profile->slug );
+	$action = sprintf( __( '%1$s&#8217;s profile was updated', 'buddypress' ), '<a href="' . $profile_link . '">' . bp_core_get_user_displayname( $user_id ) . '</a>' );
+
+	$retval = xprofile_record_activity( array(
+		'user_id'      => $user_id,
+		'action'       => $action,
+		'primary_link' => $profile_link,
+		'component'    => buddypress()->profile->id,
+		'type'         => 'updated_profile',
+	) );
+
+	return (bool) $retval;
+}
+add_action( 'xprofile_updated_profile', 'bp_xprofile_updated_profile_activity', 10, 3 );
+
+/**
+ * Add filters for xprofile activity types to Show dropdowns.
+ *
+ * @since BuddyPress (2.0.0)
+ */
+function xprofile_activity_filter_options() {
+	?>
+
+	<option value="updated_profile"><?php _e( 'Profile Updates', 'buddypress' ) ?></option>
+
+	<?php
+}
+add_action( 'bp_activity_filter_options', 'xprofile_activity_filter_options' );
diff --git a/tests/testcases/xprofile/activity.php b/tests/testcases/xprofile/activity.php
new file mode 100644
index 0000000..16a2169
--- /dev/null
+++ b/tests/testcases/xprofile/activity.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @group xprofile
+ * @group activity
+ */
+class BP_Tests_XProfile_Activity extends BP_UnitTestCase {
+	protected $updated_profile_data = array();
+
+	/**
+	 * @group bp_xprofile_updated_profile_activity
+	 */
+	public function test_bp_xprofile_updated_profile_activity_with_errors() {
+		$d = $this->setup_updated_profile_data();
+		$this->assertFalse( bp_xprofile_updated_profile_activity( $d['u'], array( $d['f']->id ), true ) );
+	}
+
+	/**
+	 * @group bp_xprofile_updated_profile_activity
+	 */
+	public function test_bp_xprofile_updated_profile_activity_throttled() {
+		$d = $this->setup_updated_profile_data();
+
+		$time = time();
+		$prev_time = date( 'Y-m-d h:i:s', $time - ( 119 * 60 ) );
+		$now_time = date( 'Y-m-d h:i:s', $time );
+
+		$this->factory->activity->create( array(
+			'user_id' => $d['u'],
+			'component' => buddypress()->profile->id,
+			'type' => 'updated_profile',
+			'date_recorded' => $prev_time,
+		) );
+
+		$this->assertFalse( bp_xprofile_updated_profile_activity( $d['u'], array( $d['f']->id ), false ) );
+	}
+
+	/**
+	 * @group bp_xprofile_updated_profile_activity
+	 */
+	public function test_bp_xprofile_updated_profile_activity_outside_of_throttle() {
+		$d = $this->setup_updated_profile_data();
+
+		$time = time();
+		$prev_time = date( 'Y-m-d h:i:s', $time - ( 121 * 60 ) );
+		$now_time = date( 'Y-m-d h:i:s', $time );
+
+		$this->factory->activity->create( array(
+			'user_id' => $d['u'],
+			'component' => buddypress()->profile->id,
+			'type' => 'updated_profile',
+			'recorded_time' => $prev_time,
+		) );
+
+		$this->assertTrue( bp_xprofile_updated_profile_activity( $d['u'], array( $d['f']->id ), false ) );
+
+		$existing = bp_activity_get( array(
+			'max' => 1,
+			'filter' => array(
+				'user_id' => $user_id,
+				'object' => buddypress()->profile->id,
+				'action' => 'updated_profile',
+			),
+		) );
+
+		$this->assertEquals( 1, $existing['total'] );
+	}
+
+	/**
+	 * @group bp_xprofile_updated_profile_activity
+	 */
+	public function test_bp_xprofile_updated_profile_activity_no_existing_activity() {
+		$d = $this->setup_updated_profile_data();
+
+		$this->assertTrue( bp_xprofile_updated_profile_activity( $d['u'], array( $d['f']->id ), false ) );
+
+		$existing = bp_activity_get( array(
+			'max' => 1,
+			'filter' => array(
+				'user_id' => $user_id,
+				'object' => buddypress()->profile->id,
+				'action' => 'updated_profile',
+			),
+		) );
+
+		$this->assertEquals( 1, $existing['total'] );
+	}
+
+	protected function setup_updated_profile_data() {
+		$this->updated_profile_data['u'] = $this->create_user();
+		$this->updated_profile_data['g'] = $this->factory->xprofile_group->create();
+		$this->updated_profile_data['f'] = $this->factory->xprofile_field->create( array(
+			'type' => 'textbox',
+			'field_group_id' => $this->updated_profile_data['g']->id,
+		) );
+
+	}
+}
