• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications.specialaccess.deviceadmin;
18 
19 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP;
21 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_THIS_DEVICE_ADMIN_APP;
22 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVE_DEVICE_ADMIN_WARNING;
23 import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_POLICIES_WARNING;
24 import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING;
25 import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED;
26 import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_AND_UNINSTALL_DEVICE_ADMIN;
27 import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_DEVICE_ADMIN;
28 import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE;
29 import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_DIALOG_TITLE;
30 import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_POSTSETUP_WARNING;
31 import static android.app.admin.DevicePolicyResources.Strings.Settings.UNINSTALL_DEVICE_ADMIN;
32 import static android.app.admin.DevicePolicyResources.Strings.Settings.USER_ADMIN_POLICIES_WARNING;
33 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ADMIN_POLICIES_WARNING;
34 
35 import android.app.Activity;
36 import android.app.ActivityManager;
37 import android.app.AppOpsManager;
38 import android.app.Dialog;
39 import android.app.admin.DeviceAdminInfo;
40 import android.app.admin.DeviceAdminReceiver;
41 import android.app.admin.DevicePolicyManager;
42 import android.app.settings.SettingsEnums;
43 import android.content.ComponentName;
44 import android.content.Context;
45 import android.content.DialogInterface;
46 import android.content.Intent;
47 import android.content.pm.ActivityInfo;
48 import android.content.pm.ApplicationInfo;
49 import android.content.pm.PackageInfo;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackageManager.NameNotFoundException;
52 import android.content.pm.ResolveInfo;
53 import android.content.pm.UserInfo;
54 import android.content.res.Resources;
55 import android.graphics.drawable.Drawable;
56 import android.os.Binder;
57 import android.os.Bundle;
58 import android.os.Handler;
59 import android.os.IBinder;
60 import android.os.RemoteCallback;
61 import android.os.RemoteException;
62 import android.os.UserHandle;
63 import android.os.UserManager;
64 import android.text.TextUtils;
65 import android.text.TextUtils.TruncateAt;
66 import android.util.EventLog;
67 import android.util.Log;
68 import android.view.Display;
69 import android.view.KeyEvent;
70 import android.view.LayoutInflater;
71 import android.view.View;
72 import android.view.ViewGroup;
73 import android.view.ViewTreeObserver;
74 import android.view.WindowManager;
75 import android.widget.Button;
76 import android.widget.ImageView;
77 import android.widget.TextView;
78 
79 import androidx.appcompat.app.AlertDialog;
80 
81 import com.android.settings.EventLogTags;
82 import com.android.settings.R;
83 import com.android.settings.fuelgauge.BatteryUtils;
84 import com.android.settings.overlay.FeatureFactory;
85 import com.android.settings.users.UserDialogs;
86 import com.android.settingslib.RestrictedLockUtils;
87 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
88 import com.android.settingslib.RestrictedLockUtilsInternal;
89 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
90 
91 import org.xmlpull.v1.XmlPullParserException;
92 
93 import java.io.IOException;
94 import java.util.ArrayList;
95 import java.util.List;
96 import java.util.Optional;
97 
98 /**
99  * A confirmation screen for enabling administractor.
100  */
101 public class DeviceAdminAdd extends CollapsingToolbarBaseActivity {
102     static final String TAG = "DeviceAdminAdd";
103 
104     static final int DIALOG_WARNING = 1;
105 
106     private static final int MAX_ADD_MSG_LINES_PORTRAIT = 5;
107     private static final int MAX_ADD_MSG_LINES_LANDSCAPE = 2;
108     private static final int MAX_ADD_MSG_LINES = 15;
109 
110     /**
111      * Optional key to map to the package name of the Device Admin.
112      * Currently only used when uninstalling an active device admin.
113      */
114     public static final String EXTRA_DEVICE_ADMIN_PACKAGE_NAME =
115             "android.app.extra.DEVICE_ADMIN_PACKAGE_NAME";
116 
117     public static final String EXTRA_CALLED_FROM_SUPPORT_DIALOG =
118             "android.app.extra.CALLED_FROM_SUPPORT_DIALOG";
119 
120     private final IBinder mToken = new Binder();
121     Handler mHandler;
122 
123     DevicePolicyManager mDPM;
124     AppOpsManager mAppOps;
125     DeviceAdminInfo mDeviceAdmin;
126     String mAddMsgText;
127 
128     ImageView mAdminIcon;
129     TextView mAdminName;
130     TextView mAdminDescription;
131     TextView mAddMsg;
132     TextView mProfileOwnerWarning;
133     ImageView mAddMsgExpander;
134     boolean mAddMsgEllipsized = true;
135     TextView mAdminWarning;
136     TextView mSupportMessage;
137     ViewGroup mAdminPolicies;
138     Button mActionButton;
139     Button mUninstallButton;
140     Button mCancelButton;
141 
142     boolean mUninstalling = false;
143     boolean mAdding;
144     boolean mRefreshing;
145     boolean mWaitingForRemoveMsg;
146     boolean mAddingProfileOwner;
147     boolean mAdminPoliciesInitialized;
148 
149     boolean mIsCalledFromSupportDialog = false;
150 
151     private LayoutInflater mLayoutInflaternflater;
152 
153     @Override
onCreate(Bundle icicle)154     protected void onCreate(Bundle icicle) {
155         super.onCreate(icicle);
156 
157         mHandler = new Handler(getMainLooper());
158 
159         mDPM = getSystemService(DevicePolicyManager.class);
160         mAppOps = getSystemService(AppOpsManager.class);
161         mLayoutInflaternflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
162         PackageManager packageManager = getPackageManager();
163 
164         if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
165             Log.w(TAG, "Cannot start ADD_DEVICE_ADMIN as a new task");
166             finish();
167             return;
168         }
169 
170         mIsCalledFromSupportDialog = getIntent().getBooleanExtra(
171                 EXTRA_CALLED_FROM_SUPPORT_DIALOG, false);
172 
173         String action = getIntent().getAction();
174         ComponentName who = (ComponentName) getIntent().getParcelableExtra(
175                 DevicePolicyManager.EXTRA_DEVICE_ADMIN);
176         if (who == null) {
177             String packageName = getIntent().getStringExtra(EXTRA_DEVICE_ADMIN_PACKAGE_NAME);
178             Optional<ComponentName> installedAdmin = findAdminWithPackageName(packageName);
179             if (!installedAdmin.isPresent()) {
180                 Log.w(TAG, "No component specified in " + action);
181                 finish();
182                 return;
183             }
184             who = installedAdmin.get();
185             mUninstalling = true;
186         }
187 
188         if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) {
189             setResult(RESULT_CANCELED);
190             setFinishOnTouchOutside(true);
191             mAddingProfileOwner = true;
192             String callingPackage = getCallingPackage();
193             if (callingPackage == null || !callingPackage.equals(who.getPackageName())) {
194                 Log.e(TAG, "Unknown or incorrect caller");
195                 finish();
196                 return;
197             }
198             try {
199                 PackageInfo packageInfo = packageManager.getPackageInfo(callingPackage, 0);
200                 if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
201                     Log.e(TAG, "Cannot set a non-system app as a profile owner");
202                     finish();
203                     return;
204                 }
205             } catch (NameNotFoundException nnfe) {
206                 Log.e(TAG, "Cannot find the package " + callingPackage);
207                 finish();
208                 return;
209             }
210         }
211 
212         ActivityInfo ai;
213         try {
214             ai = packageManager.getReceiverInfo(who, PackageManager.GET_META_DATA);
215         } catch (PackageManager.NameNotFoundException e) {
216             Log.w(TAG, "Unable to retrieve device policy " + who, e);
217             finish();
218             return;
219         }
220 
221         // When activating, make sure the given component name is actually a valid device admin.
222         // No need to check this when deactivating, because it is safe to deactivate an active
223         // invalid device admin.
224         if (!mDPM.isAdminActive(who)) {
225             List<ResolveInfo> avail = packageManager.queryBroadcastReceivers(
226                     new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
227                     PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
228             int count = avail == null ? 0 : avail.size();
229             boolean found = false;
230             for (int i = 0; i < count; i++) {
231                 ResolveInfo ri = avail.get(i);
232                 if (ai.packageName.equals(ri.activityInfo.packageName)
233                         && ai.name.equals(ri.activityInfo.name)) {
234                     try {
235                         // We didn't retrieve the meta data for all possible matches, so
236                         // need to use the activity info of this specific one that was retrieved.
237                         ri.activityInfo = ai;
238                         DeviceAdminInfo dpi = new DeviceAdminInfo(this, ri);
239                         found = true;
240                     } catch (XmlPullParserException e) {
241                         Log.w(TAG, "Bad " + ri.activityInfo, e);
242                     } catch (IOException e) {
243                         Log.w(TAG, "Bad " + ri.activityInfo, e);
244                     }
245                     break;
246                 }
247             }
248             if (!found) {
249                 Log.w(TAG, "Request to add invalid device admin: " + who);
250                 finish();
251                 return;
252             }
253         }
254 
255         ResolveInfo ri = new ResolveInfo();
256         ri.activityInfo = ai;
257         try {
258             mDeviceAdmin = new DeviceAdminInfo(this, ri);
259         } catch (XmlPullParserException e) {
260             Log.w(TAG, "Unable to retrieve device policy " + who, e);
261             finish();
262             return;
263         } catch (IOException e) {
264             Log.w(TAG, "Unable to retrieve device policy " + who, e);
265             finish();
266             return;
267         }
268 
269         // This admin already exists, an we have two options at this point.  If new policy
270         // bits are set, show the user the new list.  If nothing has changed, simply return
271         // "OK" immediately.
272         if (DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN.equals(getIntent().getAction())) {
273             mRefreshing = false;
274             if (mDPM.isAdminActive(who)) {
275                 if (mDPM.isRemovingAdmin(who, android.os.Process.myUserHandle().getIdentifier())) {
276                     Log.w(TAG, "Requested admin is already being removed: " + who);
277                     finish();
278                     return;
279                 }
280 
281                 ArrayList<DeviceAdminInfo.PolicyInfo> newPolicies = mDeviceAdmin.getUsedPolicies();
282                 for (int i = 0; i < newPolicies.size(); i++) {
283                     DeviceAdminInfo.PolicyInfo pi = newPolicies.get(i);
284                     if (!mDPM.hasGrantedPolicy(who, pi.ident)) {
285                         mRefreshing = true;
286                         break;
287                     }
288                 }
289                 if (!mRefreshing) {
290                     // Nothing changed (or policies were removed) - return immediately
291                     setResult(Activity.RESULT_OK);
292                     finish();
293                     return;
294                 }
295             }
296         }
297 
298         final CharSequence addMsgCharSequence = getIntent().getCharSequenceExtra(
299                 DevicePolicyManager.EXTRA_ADD_EXPLANATION);
300         if (addMsgCharSequence != null) {
301             mAddMsgText = addMsgCharSequence.toString();
302         }
303 
304         if (mAddingProfileOwner) {
305             // If we're trying to add a profile owner and user setup hasn't completed yet, no
306             // need to prompt for permission. Just add and finish
307             if (!mDPM.hasUserSetupCompleted()) {
308                 addAndFinish();
309                 return;
310             }
311 
312             // otherwise, only the defined default supervision profile owner or holder of
313             // supersvision role can be set after user setup.
314             if (!mDPM.isSupervisionComponent(who)) {
315                 Log.w(TAG, "Unable to set non-default profile owner post-setup " + who);
316                 finish();
317                 return;
318             }
319 
320             // Build and show the simplified dialog
321             final Dialog dialog = new AlertDialog.Builder(this)
322                     .setTitle(mDPM.getResources().getString(SET_PROFILE_OWNER_DIALOG_TITLE,
323                             () -> getString(R.string.profile_owner_add_title_simplified)))
324                     .setView(R.layout.profile_owner_add)
325                     .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
326                         public void onClick(DialogInterface dialog, int which) {
327                             addAndFinish();
328                         }
329                     })
330                     .setNegativeButton(R.string.cancel, null)
331                     .setOnDismissListener(new DialogInterface.OnDismissListener() {
332                         public void onDismiss(DialogInterface dialogInterface) {
333                             finish();
334                         }
335                     })
336                     .create();
337             dialog.show();
338 
339             String profileOwnerName =
340                     getIntent().getStringExtra(DevicePolicyManager.EXTRA_PROFILE_OWNER_NAME);
341             mActionButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
342             mActionButton.setFilterTouchesWhenObscured(true);
343             mAddMsg = dialog.findViewById(R.id.add_msg_simplified);
344             mAddMsg.setText(mAddMsgText);
345             mAdminWarning = dialog.findViewById(R.id.admin_warning_simplified);
346             mAdminWarning.setText(
347                     mDPM.getResources().getString(NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED, () ->
348                             getString(R.string.device_admin_warning_simplified,
349                                     profileOwnerName), profileOwnerName));
350             return;
351         }
352         setContentView(R.layout.device_admin_add);
353 
354         mAdminIcon = (ImageView) findViewById(R.id.admin_icon);
355         mAdminName = (TextView) findViewById(R.id.admin_name);
356         mAdminDescription = (TextView) findViewById(R.id.admin_description);
357         mProfileOwnerWarning = (TextView) findViewById(R.id.profile_owner_warning);
358 
359         mProfileOwnerWarning.setText(
360                 mDPM.getResources().getString(SET_PROFILE_OWNER_POSTSETUP_WARNING,
361                         () -> getString(R.string.adding_profile_owner_warning)));
362 
363         mAddMsg = (TextView) findViewById(R.id.add_msg);
364         mAddMsgExpander = (ImageView) findViewById(R.id.add_msg_expander);
365         final View.OnClickListener onClickListener = new View.OnClickListener() {
366             @Override
367             public void onClick(View v) {
368                 toggleMessageEllipsis(mAddMsg);
369             }
370         };
371         mAddMsgExpander.setOnClickListener(onClickListener);
372 
373         // Determine whether the message can be collapsed - getLineCount() gives the correct
374         // number of lines only after a layout pass.
375         mAddMsg.getViewTreeObserver().addOnGlobalLayoutListener(
376                 new ViewTreeObserver.OnGlobalLayoutListener() {
377                     @Override
378                     public void onGlobalLayout() {
379                         final int maxLines = getEllipsizedLines();
380                         // hide the icon if number of visible lines does not exceed maxLines
381                         boolean hideMsgExpander = mAddMsg.getLineCount() <= maxLines;
382                         mAddMsgExpander.setVisibility(hideMsgExpander ? View.GONE : View.VISIBLE);
383                         if (hideMsgExpander) {
384                             ((View) mAddMsgExpander.getParent()).invalidate();
385                         }
386                         mAddMsg.getViewTreeObserver().removeOnGlobalLayoutListener(this);
387                     }
388                 });
389 
390         // toggleMessageEllipsis also handles initial layout:
391         toggleMessageEllipsis(mAddMsg);
392 
393         mAdminWarning = (TextView) findViewById(R.id.admin_warning);
394         mAdminPolicies = (ViewGroup) findViewById(R.id.admin_policies);
395         mSupportMessage = (TextView) findViewById(R.id.admin_support_message);
396 
397         mCancelButton = (Button) findViewById(R.id.cancel_button);
398         mCancelButton.setFilterTouchesWhenObscured(true);
399         mCancelButton.setOnClickListener(new View.OnClickListener() {
400             public void onClick(View v) {
401                 EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_DECLINED_BY_USER,
402                         mDeviceAdmin.getActivityInfo().applicationInfo.uid);
403                 finish();
404             }
405         });
406 
407         mUninstallButton = (Button) findViewById(R.id.uninstall_button);
408         mUninstallButton.setText(mDPM.getResources().getString(UNINSTALL_DEVICE_ADMIN,
409                 () -> getString(R.string.uninstall_device_admin)));
410         mUninstallButton.setFilterTouchesWhenObscured(true);
411         mUninstallButton.setOnClickListener(new View.OnClickListener() {
412             public void onClick(View v) {
413                 EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_UNINSTALLED_BY_USER,
414                         mDeviceAdmin.getActivityInfo().applicationInfo.uid);
415                 mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName());
416                 finish();
417             }
418         });
419 
420         mActionButton = (Button) findViewById(R.id.action_button);
421 
422         final View restrictedAction = findViewById(R.id.restricted_action);
423         restrictedAction.setFilterTouchesWhenObscured(true);
424 
425         final View.OnClickListener restrictedActionClickListener = v -> {
426             if (!mActionButton.isEnabled()) {
427                 showPolicyTransparencyDialogIfRequired();
428                 return;
429             }
430             if (mAdding) {
431                 addAndFinish();
432             } else if (isManagedProfile(mDeviceAdmin)
433                     && mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner())) {
434                 final int userId = UserHandle.myUserId();
435                 UserDialogs.createRemoveDialog(DeviceAdminAdd.this, userId,
436                         new DialogInterface.OnClickListener() {
437                             @Override
438                             public void onClick(DialogInterface dialog, int which) {
439                                 UserManager um = UserManager.get(DeviceAdminAdd.this);
440                                 um.removeUser(userId);
441                                 finish();
442                             }
443                         }
444                 ).show();
445             } else if (mUninstalling) {
446                 mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName());
447                 finish();
448             } else if (!mWaitingForRemoveMsg) {
449                 try {
450                     // Don't allow the admin to put a dialog up in front
451                     // of us while we interact with the user.
452                     ActivityManager.getService().stopAppSwitches();
453                 } catch (RemoteException e) {
454                 }
455                 mWaitingForRemoveMsg = true;
456                 mDPM.getRemoveWarning(mDeviceAdmin.getComponent(),
457                         new RemoteCallback(new RemoteCallback.OnResultListener() {
458                             @Override
459                             public void onResult(Bundle result) {
460                                 CharSequence msg = result != null
461                                         ? result.getCharSequence(
462                                         DeviceAdminReceiver.EXTRA_DISABLE_WARNING)
463                                         : null;
464                                 continueRemoveAction(msg);
465                             }
466                         }, mHandler));
467                 // Don't want to wait too long.
468                 getWindow().getDecorView().getHandler().postDelayed(
469                         () -> continueRemoveAction(null), 2 * 1000);
470             }
471         };
472         restrictedAction.setOnKeyListener((view, keyCode, keyEvent) -> {
473             if ((keyEvent.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) == 0) {
474                 Log.e(TAG, "Can not activate device-admin with KeyEvent from non-system app.");
475                 // Consume event to suppress click.
476                 return true;
477             }
478             // Fallback to view click handler.
479             return false;
480         });
481         restrictedAction.setOnClickListener(restrictedActionClickListener);
482     }
483 
484     /**
485      * Shows a dialog to explain why the button is disabled if required.
486      */
showPolicyTransparencyDialogIfRequired()487     private void showPolicyTransparencyDialogIfRequired() {
488         if (isManagedProfile(mDeviceAdmin)
489                 && mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner())) {
490             EnforcedAdmin enforcedAdmin;
491             ComponentName adminComponent = mDPM.getProfileOwnerAsUser(getUserId());
492             if (adminComponent != null && mDPM.isOrganizationOwnedDeviceWithManagedProfile()) {
493                 enforcedAdmin = new EnforcedAdmin(adminComponent,
494                         UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(getUserId()));
495             } else {
496                 // Todo (b/151061366): Investigate this case to check if it is still viable.
497                 if (hasBaseCantRemoveProfileRestriction()) {
498                     // If DISALLOW_REMOVE_MANAGED_PROFILE is set by the system, there's no
499                     // point showing a dialog saying it's disabled by an admin.
500                     return;
501                 }
502                 enforcedAdmin = getAdminEnforcingCantRemoveProfile();
503             }
504             if (enforcedAdmin != null) {
505                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
506                         DeviceAdminAdd.this,
507                         enforcedAdmin);
508             }
509         }
510     }
511 
addAndFinish()512     void addAndFinish() {
513         try {
514             logSpecialPermissionChange(true, mDeviceAdmin.getComponent().getPackageName());
515             mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing);
516             EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER,
517                 mDeviceAdmin.getActivityInfo().applicationInfo.uid);
518 
519             unrestrictAppIfPossible(BatteryUtils.getInstance(this));
520 
521             setResult(Activity.RESULT_OK);
522         } catch (RuntimeException e) {
523             // Something bad happened...  could be that it was
524             // already set, though.
525             Log.w(TAG, "Exception trying to activate admin "
526                     + mDeviceAdmin.getComponent(), e);
527             if (mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
528                 setResult(Activity.RESULT_OK);
529             }
530         }
531         if (mAddingProfileOwner) {
532             try {
533                 mDPM.setProfileOwner(mDeviceAdmin.getComponent(), UserHandle.myUserId());
534             } catch (RuntimeException re) {
535                 setResult(Activity.RESULT_CANCELED);
536             }
537         }
538         finish();
539     }
540 
unrestrictAppIfPossible(BatteryUtils batteryUtils)541     void unrestrictAppIfPossible(BatteryUtils batteryUtils) {
542         // Unrestrict admin app if it is already been restricted
543         final String packageName = mDeviceAdmin.getComponent().getPackageName();
544         batteryUtils.clearForceAppStandby(packageName);
545     }
546 
continueRemoveAction(CharSequence msg)547     void continueRemoveAction(CharSequence msg) {
548         if (!mWaitingForRemoveMsg) {
549             return;
550         }
551         mWaitingForRemoveMsg = false;
552         if (msg == null) {
553             try {
554                 ActivityManager.getService().resumeAppSwitches();
555             } catch (RemoteException e) {
556             }
557             logSpecialPermissionChange(false, mDeviceAdmin.getComponent().getPackageName());
558             mDPM.removeActiveAdmin(mDeviceAdmin.getComponent());
559             finish();
560         } else {
561             try {
562                 // Continue preventing anything from coming in front.
563                 ActivityManager.getService().stopAppSwitches();
564             } catch (RemoteException e) {
565             }
566             Bundle args = new Bundle();
567             args.putCharSequence(
568                     DeviceAdminReceiver.EXTRA_DISABLE_WARNING, msg);
569             showDialog(DIALOG_WARNING, args);
570         }
571     }
572 
logSpecialPermissionChange(boolean allow, String packageName)573     void logSpecialPermissionChange(boolean allow, String packageName) {
574         int logCategory = allow ? SettingsEnums.APP_SPECIAL_PERMISSION_ADMIN_ALLOW :
575                 SettingsEnums.APP_SPECIAL_PERMISSION_ADMIN_DENY;
576         FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().action(
577                 SettingsEnums.PAGE_UNKNOWN,
578                 logCategory,
579                 SettingsEnums.PAGE_UNKNOWN,
580                 packageName,
581                 0);
582     }
583 
584     @Override
onResume()585     protected void onResume() {
586         super.onResume();
587         mActionButton.setEnabled(true);
588         if (!mAddingProfileOwner) {
589             updateInterface();
590         }
591         // As long as we are running, don't let anyone overlay stuff on top of the screen.
592         mAppOps.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, true, mToken);
593         mAppOps.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, true, mToken);
594 
595     }
596 
597     @Override
onPause()598     protected void onPause() {
599         super.onPause();
600         // This just greys out the button. The actual listener is attached to R.id.restricted_action
601         mActionButton.setEnabled(false);
602         mAppOps.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, false, mToken);
603         mAppOps.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW, false, mToken);
604         try {
605             ActivityManager.getService().resumeAppSwitches();
606         } catch (RemoteException e) {
607         }
608     }
609 
610     @Override
onUserLeaveHint()611     protected void onUserLeaveHint() {
612         super.onUserLeaveHint();
613         // In case this is triggered from support dialog, finish this activity once the user leaves
614         // so that this won't appear as a background next time support dialog is triggered. This
615         // is because the support dialog activity and this belong to the same task and we can't
616         // start this in new activity since we need to know the calling package in this activity.
617         if (mIsCalledFromSupportDialog) {
618             finish();
619         }
620     }
621 
622     @Override
onCreateDialog(int id, Bundle args)623     protected Dialog onCreateDialog(int id, Bundle args) {
624         switch (id) {
625             case DIALOG_WARNING: {
626                 CharSequence msg = args.getCharSequence(DeviceAdminReceiver.EXTRA_DISABLE_WARNING);
627                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
628                 builder.setMessage(msg);
629                 builder.setPositiveButton(R.string.dlg_ok,
630                         new DialogInterface.OnClickListener() {
631                     public void onClick(DialogInterface dialog, int which) {
632                         try {
633                             ActivityManager.getService().resumeAppSwitches();
634                         } catch (RemoteException e) {
635                         }
636                         mDPM.removeActiveAdmin(mDeviceAdmin.getComponent());
637                         finish();
638                     }
639                 });
640                 builder.setNegativeButton(R.string.dlg_cancel, null);
641                 return builder.create();
642             }
643             default:
644                 return super.onCreateDialog(id, args);
645 
646         }
647     }
648 
updateInterface()649     void updateInterface() {
650         findViewById(com.android.settingslib.widget.restricted.R.id.restricted_icon)
651                 .setVisibility(View.GONE);
652         mAdminIcon.setImageDrawable(mDeviceAdmin.loadIcon(getPackageManager()));
653         mAdminName.setText(mDeviceAdmin.loadLabel(getPackageManager()));
654         try {
655             mAdminDescription.setText(
656                     mDeviceAdmin.loadDescription(getPackageManager()));
657             mAdminDescription.setVisibility(View.VISIBLE);
658         } catch (Resources.NotFoundException e) {
659             mAdminDescription.setVisibility(View.GONE);
660         }
661         if (!TextUtils.isEmpty(mAddMsgText)) {
662             mAddMsg.setText(mAddMsgText);
663             mAddMsg.setVisibility(View.VISIBLE);
664         } else {
665             mAddMsg.setVisibility(View.GONE);
666             mAddMsgExpander.setVisibility(View.GONE);
667         }
668         if (!mRefreshing && !mAddingProfileOwner
669                 && mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
670             mAdding = false;
671             final boolean isProfileOwner =
672                     mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner());
673             final boolean isManagedProfile = isManagedProfile(mDeviceAdmin);
674             if (isProfileOwner && isManagedProfile) {
675                 // Profile owner in a managed profile, user can remove profile to disable admin.
676                 mAdminWarning.setText(mDPM.getResources().getString(
677                         WORK_PROFILE_ADMIN_POLICIES_WARNING,
678                         () -> getString(R.string.admin_profile_owner_message)));
679                 mActionButton.setText(mDPM.getResources().getString(REMOVE_WORK_PROFILE,
680                         () -> getString(R.string.remove_managed_profile_label)));
681 
682                 final EnforcedAdmin admin = getAdminEnforcingCantRemoveProfile();
683                 final boolean hasBaseRestriction = hasBaseCantRemoveProfileRestriction();
684                 if ((hasBaseRestriction && mDPM.isOrganizationOwnedDeviceWithManagedProfile())
685                         || (admin != null && !hasBaseRestriction)) {
686                     findViewById(com.android.settingslib.widget.restricted.R.id.restricted_icon)
687                             .setVisibility(View.VISIBLE);
688                 }
689                 mActionButton.setEnabled(admin == null && !hasBaseRestriction);
690             } else if (isProfileOwner || mDeviceAdmin.getComponent().equals(
691                             mDPM.getDeviceOwnerComponentOnCallingUser())) {
692                 // Profile owner in a user or device owner, user can't disable admin.
693                 if (isProfileOwner) {
694                     // Show profile owner in a user description.
695                     mAdminWarning.setText(mDPM.getResources().getString(USER_ADMIN_POLICIES_WARNING,
696                             () -> getString(R.string.admin_profile_owner_user_message)));
697                 } else {
698                     // Show device owner description.
699                     if (isFinancedDevice()) {
700                         mAdminWarning.setText(R.string.admin_financed_message);
701                     } else {
702                         mAdminWarning.setText(mDPM.getResources().getString(
703                                 DEVICE_ADMIN_POLICIES_WARNING,
704                                 () -> getString(R.string.admin_device_owner_message)));
705                     }
706                 }
707                 mActionButton.setText(mDPM.getResources().getString(REMOVE_DEVICE_ADMIN,
708                         () -> getString(R.string.remove_device_admin)));
709                 mActionButton.setEnabled(false);
710             } else {
711                 addDeviceAdminPolicies(false /* showDescription */);
712                 CharSequence label = mDeviceAdmin.getActivityInfo().applicationInfo.loadLabel(
713                         getPackageManager());
714                 mAdminWarning.setText(mDPM.getResources().getString(ACTIVE_DEVICE_ADMIN_WARNING,
715                         () -> getString(R.string.device_admin_status, label), label));
716                 setTitle(R.string.active_device_admin_msg);
717                 if (mUninstalling) {
718                     mActionButton.setText(mDPM.getResources().getString(
719                             REMOVE_AND_UNINSTALL_DEVICE_ADMIN,
720                             () -> getString(R.string.remove_and_uninstall_device_admin)));
721                 } else {
722                     mActionButton.setText(mDPM.getResources().getString(REMOVE_DEVICE_ADMIN,
723                             () -> getString(R.string.remove_device_admin)));
724                 }
725             }
726             CharSequence supportMessage = mDPM.getLongSupportMessageForUser(
727                     mDeviceAdmin.getComponent(), UserHandle.myUserId());
728             if (!TextUtils.isEmpty(supportMessage)) {
729                 mSupportMessage.setText(supportMessage);
730                 mSupportMessage.setVisibility(View.VISIBLE);
731             } else {
732                 mSupportMessage.setVisibility(View.GONE);
733             }
734         } else {
735             addDeviceAdminPolicies(true /* showDescription */);
736             CharSequence label = mDeviceAdmin.getActivityInfo()
737                     .applicationInfo.loadLabel(getPackageManager());
738             mAdminWarning.setText(
739                     mDPM.getResources().getString(NEW_DEVICE_ADMIN_WARNING, () ->
740                     getString(R.string.device_admin_warning, label
741                     ), label));
742             setTitle(mDPM.getResources().getString(ACTIVATE_DEVICE_ADMIN_APP,
743                     () -> getString(R.string.add_device_admin_msg)));
744             mActionButton.setText(mDPM.getResources().getString(ACTIVATE_THIS_DEVICE_ADMIN_APP,
745                     () -> getString(R.string.add_device_admin)));
746             if (isAdminUninstallable()) {
747                 mUninstallButton.setVisibility(View.VISIBLE);
748             }
749             mSupportMessage.setVisibility(View.GONE);
750             mAdding = true;
751         }
752     }
753 
getAdminEnforcingCantRemoveProfile()754     private EnforcedAdmin getAdminEnforcingCantRemoveProfile() {
755         // Removing a managed profile is disallowed if DISALLOW_REMOVE_MANAGED_PROFILE
756         // is set in the parent rather than the user itself.
757         return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(this,
758                 UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, getParentUserId());
759     }
760 
hasBaseCantRemoveProfileRestriction()761     private boolean hasBaseCantRemoveProfileRestriction() {
762         return RestrictedLockUtilsInternal.hasBaseUserRestriction(this,
763                 UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, getParentUserId());
764     }
765 
getParentUserId()766     private int getParentUserId() {
767         return UserManager.get(this).getProfileParent(UserHandle.myUserId()).id;
768     }
769 
addDeviceAdminPolicies(boolean showDescription)770     private void addDeviceAdminPolicies(boolean showDescription) {
771         if (!mAdminPoliciesInitialized) {
772             boolean isAdminUser = UserManager.get(this).isAdminUser();
773             for (DeviceAdminInfo.PolicyInfo pi : mDeviceAdmin.getUsedPolicies()) {
774                 int descriptionId = isAdminUser ? pi.description : pi.descriptionForSecondaryUsers;
775                 int labelId = isAdminUser ? pi.label : pi.labelForSecondaryUsers;
776                 View view = getPermissionItemView(getText(labelId),
777                         showDescription ? getText(descriptionId) : "");
778                 mAdminPolicies.addView(view);
779             }
780             mAdminPoliciesInitialized = true;
781         }
782     }
783 
784     /**
785      * Utility to retrieve a view displaying a single permission.  This provides
786      * the UI layout for permissions.
787      */
getPermissionItemView(CharSequence grpName, CharSequence description)788     private View getPermissionItemView(CharSequence grpName, CharSequence description) {
789         Drawable icon = this.getDrawable(com.android.internal.R.drawable.ic_text_dot);
790         View permView = mLayoutInflaternflater.inflate(R.layout.app_permission_item, null);
791         TextView permGrpView = permView.findViewById(R.id.permission_group);
792         TextView permDescView = permView.findViewById(R.id.permission_list);
793         ImageView imgView = (ImageView) permView.findViewById(R.id.perm_icon);
794 
795         imgView.setImageDrawable(icon);
796         if (grpName != null) {
797             permGrpView.setText(grpName);
798             permDescView.setText(description);
799         } else {
800             permGrpView.setText(description);
801             permDescView.setVisibility(View.GONE);
802         }
803         return permView;
804     }
805 
toggleMessageEllipsis(View v)806     void toggleMessageEllipsis(View v) {
807         TextView tv = (TextView) v;
808 
809         mAddMsgEllipsized = ! mAddMsgEllipsized;
810         tv.setEllipsize(mAddMsgEllipsized ? TruncateAt.END : null);
811         tv.setMaxLines(mAddMsgEllipsized ? getEllipsizedLines() : MAX_ADD_MSG_LINES);
812 
813         mAddMsgExpander.setImageResource(mAddMsgEllipsized ?
814             com.android.internal.R.drawable.expander_ic_minimized :
815             com.android.internal.R.drawable.expander_ic_maximized);
816     }
817 
getEllipsizedLines()818     int getEllipsizedLines() {
819         Display d = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
820                     .getDefaultDisplay();
821 
822         return d.getHeight() > d.getWidth() ?
823             MAX_ADD_MSG_LINES_PORTRAIT : MAX_ADD_MSG_LINES_LANDSCAPE;
824     }
825 
826     /**
827      * @return true if adminInfo is running in a managed profile.
828      */
isManagedProfile(DeviceAdminInfo adminInfo)829     private boolean isManagedProfile(DeviceAdminInfo adminInfo) {
830         UserManager um = UserManager.get(this);
831         UserInfo info = um.getUserInfo(
832                 UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid));
833         return info != null ? info.isManagedProfile() : false;
834     }
835 
isFinancedDevice()836     private boolean isFinancedDevice() {
837         return mDPM.isDeviceManaged() && mDPM.getDeviceOwnerType(
838                 mDPM.getDeviceOwnerComponentOnAnyUser()) == DEVICE_OWNER_TYPE_FINANCED;
839     }
840 
841     /**
842      * @return an {@link Optional} containing the admin with a given package name, if it exists,
843      *         or {@link Optional#empty()} otherwise.
844      */
findAdminWithPackageName(String packageName)845     private Optional<ComponentName> findAdminWithPackageName(String packageName) {
846         List<ComponentName> admins = mDPM.getActiveAdmins();
847         if (admins == null) {
848             return Optional.empty();
849         }
850         return admins.stream().filter(i -> i.getPackageName().equals(packageName)).findAny();
851     }
852 
isAdminUninstallable()853     private boolean isAdminUninstallable() {
854         // System apps can't be uninstalled.
855         return !mDeviceAdmin.getActivityInfo().applicationInfo.isSystemApp();
856     }
857 }
858