Merge "Fix bug #16306535 The arrows rotate in the opposite direction... of the direction implied by the iconography (clockwise)" into lmp-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a50343e..3594795 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -973,6 +973,15 @@
             </intent-filter>
         </activity>
 
+        <activity-alias android:name="SetProfileOwner"
+                android:label="@string/profile_owner_add_title"
+                android:targetActivity="DeviceAdminAdd">
+            <intent-filter android:priority="1000">
+                <action android:name="android.app.action.SET_PROFILE_OWNER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+
         <activity android:name="IccLockSettings"
                 android:label="@string/sim_lock_settings"
                 android:theme="@style/Theme.SubSettingsDialogWhenLarge"
diff --git a/res/layout/device_admin_add.xml b/res/layout/device_admin_add.xml
index c54ecce..25b7f01 100644
--- a/res/layout/device_admin_add.xml
+++ b/res/layout/device_admin_add.xml
@@ -39,6 +39,7 @@
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 android:layout_marginTop="16dip">
+
                 <ImageView android:id="@+id/admin_icon"
                     android:layout_width="@android:dimen/app_icon_size"
                     android:layout_height="@android:dimen/app_icon_size"
@@ -46,6 +47,7 @@
                     android:layout_gravity="center_vertical"
                     android:scaleType="fitCenter"
                     android:contentDescription="@null" />
+
                 <TextView android:id="@+id/admin_name"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
@@ -62,21 +64,32 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
+
+                <TextView android:id="@+id/profile_owner_warning"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:padding="0dp"
+                    android:visibility="gone"
+                    android:text="@string/adding_profile_owner_warning" />
+
                 <TextView android:id="@+id/admin_description"
                     android:layout_width="fill_parent"
                     android:layout_height="0dip"
                     android:layout_weight="0"
                     android:padding="0dip" />
+
                 <LinearLayout
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:orientation="horizontal"
                     android:gravity="center_vertical">
+
                     <ImageView android:id="@+id/add_msg_expander"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:gravity="center_vertical"
                         android:contentDescription="@null" />
+
                     <TextView android:id="@+id/add_msg"
                         android:layout_width="fill_parent"
                         android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5a87472..d3fe9e7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4402,6 +4402,11 @@
         the app <xliff:g id="app_name">%1$s</xliff:g> to perform the
         following operations:</string>
 
+    <!-- Title for screen to set a profile owner [CHAR LIMIT=40] -->
+    <string name="profile_owner_add_title">Profile owner</string>
+    <!-- Warning when trying to add a profile owner admin after setup has completed. [CHAR LIMIT=none] -->
+    <string name="adding_profile_owner_warning" translatable="false">This application wants to assume COMPLETE control of this user, including restricting critical operations. Only allow this if you trust this application.</string>
+
     <!-- Name to assign to a Network Access Point that was saved without a name -->
     <string name="untitled_apn">Untitled</string>
 
diff --git a/src/com/android/settings/DeviceAdminAdd.java b/src/com/android/settings/DeviceAdminAdd.java
index 59f2af9..0bd548f 100644
--- a/src/com/android/settings/DeviceAdminAdd.java
+++ b/src/com/android/settings/DeviceAdminAdd.java
@@ -17,6 +17,7 @@
 package com.android.settings;
 
 import android.app.AppOpsManager;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.app.Activity;
@@ -38,6 +39,7 @@
 import android.os.Handler;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.text.TextUtils.TruncateAt;
 import android.util.EventLog;
 import android.util.Log;
@@ -69,11 +71,13 @@
     AppOpsManager mAppOps;
     DeviceAdminInfo mDeviceAdmin;
     CharSequence mAddMsgText;
+    String mProfileOwnerName;
 
     ImageView mAdminIcon;
     TextView mAdminName;
     TextView mAdminDescription;
     TextView mAddMsg;
+    TextView mProfileOwnerWarning;
     ImageView mAddMsgExpander;
     boolean mAddMsgEllipsized = true;
     TextView mAdminWarning;
@@ -87,6 +91,7 @@
     boolean mAdding;
     boolean mRefreshing;
     boolean mWaitingForRemoveMsg;
+    boolean mAddingProfileOwner;
     int mCurSysAppOpMode;
     int mCurToastAppOpMode;
 
@@ -105,19 +110,32 @@
             return;
         }
 
-        ComponentName cn = (ComponentName)getIntent().getParcelableExtra(
+        String action = getIntent().getAction();
+        ComponentName who = (ComponentName)getIntent().getParcelableExtra(
                 DevicePolicyManager.EXTRA_DEVICE_ADMIN);
-        if (cn == null) {
-            Log.w(TAG, "No component specified in " + getIntent().getAction());
+        if (who == null) {
+            Log.w(TAG, "No component specified in " + action);
             finish();
             return;
         }
 
+        if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) {
+            mAddingProfileOwner = true;
+            mProfileOwnerName =
+                    getIntent().getStringExtra(DevicePolicyManager.EXTRA_PROFILE_OWNER_NAME);
+            String callingPackage = getCallingPackage();
+            if (callingPackage == null || !callingPackage.equals(who.getPackageName())) {
+                Log.e(TAG, "Unknown or incorrect caller");
+                finish();
+                return;
+            }
+        }
+
         ActivityInfo ai;
         try {
-            ai = getPackageManager().getReceiverInfo(cn, PackageManager.GET_META_DATA);
+            ai = getPackageManager().getReceiverInfo(who, PackageManager.GET_META_DATA);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+            Log.w(TAG, "Unable to retrieve device policy " + who, e);
             finish();
             return;
         }
@@ -125,7 +143,7 @@
         // When activating, make sure the given component name is actually a valid device admin.
         // No need to check this when deactivating, because it is safe to deactivate an active
         // invalid device admin.
-        if (!mDPM.isAdminActive(cn)) {
+        if (!mDPM.isAdminActive(who)) {
             List<ResolveInfo> avail = getPackageManager().queryBroadcastReceivers(
                     new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
                     PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
@@ -150,7 +168,7 @@
                 }
             }
             if (!found) {
-                Log.w(TAG, "Request to add invalid device admin: " + cn);
+                Log.w(TAG, "Request to add invalid device admin: " + who);
                 finish();
                 return;
             }
@@ -161,11 +179,11 @@
         try {
             mDeviceAdmin = new DeviceAdminInfo(this, ri);
         } catch (XmlPullParserException e) {
-            Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+            Log.w(TAG, "Unable to retrieve device policy " + who, e);
             finish();
             return;
         } catch (IOException e) {
-            Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+            Log.w(TAG, "Unable to retrieve device policy " + who, e);
             finish();
             return;
         }
@@ -175,11 +193,11 @@
         // "OK" immediately.
         if (DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN.equals(getIntent().getAction())) {
             mRefreshing = false;
-            if (mDPM.isAdminActive(cn)) {
+            if (mDPM.isAdminActive(who)) {
                 ArrayList<DeviceAdminInfo.PolicyInfo> newPolicies = mDeviceAdmin.getUsedPolicies();
                 for (int i = 0; i < newPolicies.size(); i++) {
                     DeviceAdminInfo.PolicyInfo pi = newPolicies.get(i);
-                    if (!mDPM.hasGrantedPolicy(cn, pi.ident)) {
+                    if (!mDPM.hasGrantedPolicy(who, pi.ident)) {
                         mRefreshing = true;
                         break;
                     }
@@ -192,6 +210,14 @@
                 }
             }
         }
+
+        // If we're trying to add a profile owner and user setup hasn't completed yet, no
+        // need to prompt for permission. Just add and finish.
+        if (mAddingProfileOwner && !mDPM.hasUserSetupCompleted()) {
+            addAndFinish();
+            return;
+        }
+
         mAddMsgText = getIntent().getCharSequenceExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION);
 
         setContentView(R.layout.device_admin_add);
@@ -199,6 +225,7 @@
         mAdminIcon = (ImageView)findViewById(R.id.admin_icon);
         mAdminName = (TextView)findViewById(R.id.admin_name);
         mAdminDescription = (TextView)findViewById(R.id.admin_description);
+        mProfileOwnerWarning = (TextView) findViewById(R.id.profile_owner_warning);
 
         mAddMsg = (TextView)findViewById(R.id.add_msg);
         mAddMsgExpander = (ImageView) findViewById(R.id.add_msg_expander);
@@ -225,21 +252,7 @@
         mActionButton.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 if (mAdding) {
-                    try {
-                        mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing);
-                        setResult(Activity.RESULT_OK);
-                        EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER,
-                            mDeviceAdmin.getActivityInfo().applicationInfo.uid);
-                    } catch (RuntimeException e) {
-                        // Something bad happened...  could be that it was
-                        // already set, though.
-                        Log.w(TAG, "Exception trying to activate admin "
-                                + mDeviceAdmin.getComponent(), e);
-                        if (mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
-                            setResult(Activity.RESULT_OK);
-                        }
-                    }
-                    finish();
+                    addAndFinish();
                 } else if (!mWaitingForRemoveMsg) {
                     try {
                         // Don't allow the admin to put a dialog up in front
@@ -270,6 +283,32 @@
         });
     }
 
+    void addAndFinish() {
+        try {
+            mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing);
+            EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER,
+                mDeviceAdmin.getActivityInfo().applicationInfo.uid);
+            setResult(Activity.RESULT_OK);
+        } catch (RuntimeException e) {
+            // Something bad happened...  could be that it was
+            // already set, though.
+            Log.w(TAG, "Exception trying to activate admin "
+                    + mDeviceAdmin.getComponent(), e);
+            if (mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
+                setResult(Activity.RESULT_OK);
+            }
+        }
+        if (mAddingProfileOwner) {
+            try {
+                mDPM.setProfileOwner(mDeviceAdmin.getComponent(),
+                        mProfileOwnerName, UserHandle.myUserId());
+            } catch (RuntimeException re) {
+                setResult(Activity.RESULT_CANCELED);
+            }
+        }
+        finish();
+    }
+
     void continueRemoveAction(CharSequence msg) {
         if (!mWaitingForRemoveMsg) {
             return;
@@ -367,6 +406,9 @@
         } catch (Resources.NotFoundException e) {
             mAdminDescription.setVisibility(View.GONE);
         }
+        if (mAddingProfileOwner) {
+            mProfileOwnerWarning.setVisibility(View.VISIBLE);
+        }
         if (mAddMsgText != null) {
             mAddMsg.setText(mAddMsgText);
             mAddMsg.setVisibility(View.VISIBLE);
@@ -374,7 +416,8 @@
             mAddMsg.setVisibility(View.GONE);
             mAddMsgExpander.setVisibility(View.GONE);
         }
-        if (!mRefreshing && mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
+        if (!mRefreshing && !mAddingProfileOwner
+                && mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
             if (mActivePolicies.size() == 0) {
                 ArrayList<DeviceAdminInfo.PolicyInfo> policies = mDeviceAdmin.getUsedPolicies();
                 for (int i=0; i<policies.size(); i++) {
@@ -407,7 +450,11 @@
             setViewVisibility(mActivePolicies, View.GONE);
             mAdminWarning.setText(getString(R.string.device_admin_warning,
                     mDeviceAdmin.getActivityInfo().applicationInfo.loadLabel(getPackageManager())));
-            setTitle(getText(R.string.add_device_admin_msg));
+            if (mAddingProfileOwner) {
+                setTitle(getText(R.string.profile_owner_add_title));
+            } else {
+                setTitle(getText(R.string.add_device_admin_msg));
+            }
             mActionButton.setText(getText(R.string.add_device_admin));
             mAdding = true;
         }
diff --git a/src/com/android/settings/DeviceAdminSettings.java b/src/com/android/settings/DeviceAdminSettings.java
index 11f8b00..f597b9a 100644
--- a/src/com/android/settings/DeviceAdminSettings.java
+++ b/src/com/android/settings/DeviceAdminSettings.java
@@ -50,13 +50,14 @@
 
 public class DeviceAdminSettings extends ListFragment {
     static final String TAG = "DeviceAdminSettings";
-    
+
     static final int DIALOG_WARNING = 1;
-    
+
     DevicePolicyManager mDPM;
     final HashSet<ComponentName> mActiveAdmins = new HashSet<ComponentName>();
     final ArrayList<DeviceAdminInfo> mAvailableAdmins = new ArrayList<DeviceAdminInfo>();
     String mDeviceOwnerPkg;
+    ComponentName mProfileOwnerComponent;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -84,6 +85,7 @@
         if (mDeviceOwnerPkg != null && !mDPM.isDeviceOwner(mDeviceOwnerPkg)) {
             mDeviceOwnerPkg = null;
         }
+        mProfileOwnerComponent = mDPM.getProfileOwner();
         updateList();
     }
 
@@ -139,7 +141,7 @@
                 Log.w(TAG, "Skipping " + ri.activityInfo, e);
             }
         }
-        
+
         getListView().setAdapter(new PolicyListAdapter());
     }
 
@@ -158,10 +160,10 @@
         CheckBox checkbox;
         TextView description;
     }
-    
+
     class PolicyListAdapter extends BaseAdapter {
         final LayoutInflater mInflater;
-        
+
         PolicyListAdapter() {
             mInflater = (LayoutInflater)
                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -170,7 +172,7 @@
         public boolean hasStableIds() {
             return true;
         }
-        
+
         public int getCount() {
             return mAvailableAdmins.size();
         }
@@ -189,8 +191,11 @@
 
         public boolean isEnabled(int position) {
             DeviceAdminInfo info = mAvailableAdmins.get(position);
-            if (mActiveAdmins.contains(info.getComponent())
-                    && info.getPackageName().equals(mDeviceOwnerPkg)) {
+            String packageName = info.getPackageName();
+            ComponentName component = info.getComponent();
+            if (mActiveAdmins.contains(component)
+                    && (packageName.equals(mDeviceOwnerPkg)
+                            || component.equals(mProfileOwnerComponent))) {
                 return false;
             } else {
                 return true;
@@ -207,7 +212,7 @@
             bindView(v, position);
             return v;
         }
-        
+
         public View newView(ViewGroup parent) {
             View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
             ViewHolder h = new ViewHolder();
@@ -218,7 +223,7 @@
             v.setTag(h);
             return v;
         }
-        
+
         public void bindView(View view, int position) {
             final Activity activity = getActivity();
             ViewHolder vh = (ViewHolder) view.getTag();
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index 9fb42c5..429c01c 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -123,16 +123,9 @@
     private PreferenceCategory mPrintServicesCategory;
 
     private PrintJobsController mPrintJobsController;
-    private Context mContext;
     private UserSpinnerAdapter mProfileSpinnerAdapter;
 
     @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        mContext = activity;
-    }
-
-    @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.print_settings);
@@ -200,10 +193,10 @@
             ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
             final int count = userProfiles.size();
             for (int i = 0; i < count; i++) {
-                userDetails.add(new UserDetails(userProfiles.get(i), um, mContext));
+                userDetails.add(new UserDetails(userProfiles.get(i), um, getActivity()));
             }
 
-            mProfileSpinnerAdapter = new UserSpinnerAdapter(mContext, userDetails);
+            mProfileSpinnerAdapter = new UserSpinnerAdapter(getActivity(), userDetails);
             spinner.setAdapter(mProfileSpinnerAdapter);
             spinner.setOnItemSelectedListener(this);
             setPinnedHeaderView(spinner);
@@ -316,7 +309,7 @@
         if (selectedUser.getIdentifier() != UserHandle.myUserId()) {
             Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivityAsUser(intent, selectedUser);
+            getActivity().startActivityAsUser(intent, selectedUser);
             getActivity().finish();
         }
     }