DeviceAdminAdd.java revision 080f8db85c62672344375ca70c53086b891bce52
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
17package com.android.settings;
18
19import android.app.AppOpsManager;
20
21import org.xmlpull.v1.XmlPullParserException;
22
23import android.app.Activity;
24import android.app.ActivityManagerNative;
25import android.app.AlertDialog;
26import android.app.Dialog;
27import android.app.admin.DeviceAdminInfo;
28import android.app.admin.DeviceAdminReceiver;
29import android.app.admin.DevicePolicyManager;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
34import android.content.pm.ActivityInfo;
35import android.content.pm.ApplicationInfo;
36import android.content.pm.PackageInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.res.Resources;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.RemoteCallback;
44import android.os.RemoteException;
45import android.os.UserHandle;
46import android.text.TextUtils.TruncateAt;
47import android.util.EventLog;
48import android.util.Log;
49import android.view.Display;
50import android.view.View;
51import android.view.ViewGroup;
52import android.view.WindowManager;
53import android.widget.AppSecurityPermissions;
54import android.widget.Button;
55import android.widget.ImageView;
56import android.widget.TextView;
57
58import java.io.IOException;
59import java.util.ArrayList;
60import java.util.List;
61
62public class DeviceAdminAdd extends Activity {
63    static final String TAG = "DeviceAdminAdd";
64
65    static final int DIALOG_WARNING = 1;
66
67    private static final int MAX_ADD_MSG_LINES_PORTRAIT = 5;
68    private static final int MAX_ADD_MSG_LINES_LANDSCAPE = 2;
69    private static final int MAX_ADD_MSG_LINES = 15;
70
71    Handler mHandler;
72
73    DevicePolicyManager mDPM;
74    AppOpsManager mAppOps;
75    DeviceAdminInfo mDeviceAdmin;
76    CharSequence mAddMsgText;
77    String mProfileOwnerName;
78
79    ImageView mAdminIcon;
80    TextView mAdminName;
81    TextView mAdminDescription;
82    TextView mAddMsg;
83    TextView mProfileOwnerWarning;
84    ImageView mAddMsgExpander;
85    boolean mAddMsgEllipsized = true;
86    TextView mAdminWarning;
87    ViewGroup mAdminPolicies;
88    Button mActionButton;
89    Button mCancelButton;
90
91    final ArrayList<View> mAddingPolicies = new ArrayList<View>();
92    final ArrayList<View> mActivePolicies = new ArrayList<View>();
93
94    boolean mAdding;
95    boolean mRefreshing;
96    boolean mWaitingForRemoveMsg;
97    boolean mAddingProfileOwner;
98    int mCurSysAppOpMode;
99    int mCurToastAppOpMode;
100
101    @Override
102    protected void onCreate(Bundle icicle) {
103        super.onCreate(icicle);
104
105        mHandler = new Handler(getMainLooper());
106
107        mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
108        mAppOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
109        PackageManager packageManager = getPackageManager();
110
111        if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
112            Log.w(TAG, "Cannot start ADD_DEVICE_ADMIN as a new task");
113            finish();
114            return;
115        }
116
117        String action = getIntent().getAction();
118        ComponentName who = (ComponentName)getIntent().getParcelableExtra(
119                DevicePolicyManager.EXTRA_DEVICE_ADMIN);
120        if (who == null) {
121            Log.w(TAG, "No component specified in " + action);
122            finish();
123            return;
124        }
125
126        if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) {
127            setResult(RESULT_CANCELED);
128            setFinishOnTouchOutside(true);
129            mAddingProfileOwner = true;
130            mProfileOwnerName =
131                    getIntent().getStringExtra(DevicePolicyManager.EXTRA_PROFILE_OWNER_NAME);
132            String callingPackage = getCallingPackage();
133            if (callingPackage == null || !callingPackage.equals(who.getPackageName())) {
134                Log.e(TAG, "Unknown or incorrect caller");
135                finish();
136                return;
137            }
138            try {
139                PackageInfo packageInfo = packageManager.getPackageInfo(callingPackage, 0);
140                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
141                    Log.e(TAG, "Cannot set a non-system app as a profile owner");
142                    finish();
143                    return;
144                }
145            } catch (NameNotFoundException nnfe) {
146                Log.e(TAG, "Cannot find the package " + callingPackage);
147                finish();
148                return;
149            }
150        }
151
152        ActivityInfo ai;
153        try {
154            ai = packageManager.getReceiverInfo(who, PackageManager.GET_META_DATA);
155        } catch (PackageManager.NameNotFoundException e) {
156            Log.w(TAG, "Unable to retrieve device policy " + who, e);
157            finish();
158            return;
159        }
160
161        // When activating, make sure the given component name is actually a valid device admin.
162        // No need to check this when deactivating, because it is safe to deactivate an active
163        // invalid device admin.
164        if (!mDPM.isAdminActive(who)) {
165            List<ResolveInfo> avail = packageManager.queryBroadcastReceivers(
166                    new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
167                    PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
168            int count = avail == null ? 0 : avail.size();
169            boolean found = false;
170            for (int i=0; i<count; i++) {
171                ResolveInfo ri = avail.get(i);
172                if (ai.packageName.equals(ri.activityInfo.packageName)
173                        && ai.name.equals(ri.activityInfo.name)) {
174                    try {
175                        // We didn't retrieve the meta data for all possible matches, so
176                        // need to use the activity info of this specific one that was retrieved.
177                        ri.activityInfo = ai;
178                        DeviceAdminInfo dpi = new DeviceAdminInfo(this, ri);
179                        found = true;
180                    } catch (XmlPullParserException e) {
181                        Log.w(TAG, "Bad " + ri.activityInfo, e);
182                    } catch (IOException e) {
183                        Log.w(TAG, "Bad " + ri.activityInfo, e);
184                    }
185                    break;
186                }
187            }
188            if (!found) {
189                Log.w(TAG, "Request to add invalid device admin: " + who);
190                finish();
191                return;
192            }
193        }
194
195        ResolveInfo ri = new ResolveInfo();
196        ri.activityInfo = ai;
197        try {
198            mDeviceAdmin = new DeviceAdminInfo(this, ri);
199        } catch (XmlPullParserException e) {
200            Log.w(TAG, "Unable to retrieve device policy " + who, e);
201            finish();
202            return;
203        } catch (IOException e) {
204            Log.w(TAG, "Unable to retrieve device policy " + who, e);
205            finish();
206            return;
207        }
208
209        // This admin already exists, an we have two options at this point.  If new policy
210        // bits are set, show the user the new list.  If nothing has changed, simply return
211        // "OK" immediately.
212        if (DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN.equals(getIntent().getAction())) {
213            mRefreshing = false;
214            if (mDPM.isAdminActive(who)) {
215                ArrayList<DeviceAdminInfo.PolicyInfo> newPolicies = mDeviceAdmin.getUsedPolicies();
216                for (int i = 0; i < newPolicies.size(); i++) {
217                    DeviceAdminInfo.PolicyInfo pi = newPolicies.get(i);
218                    if (!mDPM.hasGrantedPolicy(who, pi.ident)) {
219                        mRefreshing = true;
220                        break;
221                    }
222                }
223                if (!mRefreshing) {
224                    // Nothing changed (or policies were removed) - return immediately
225                    setResult(Activity.RESULT_OK);
226                    finish();
227                    return;
228                }
229            }
230        }
231
232        // If we're trying to add a profile owner and user setup hasn't completed yet, no
233        // need to prompt for permission. Just add and finish.
234        if (mAddingProfileOwner && !mDPM.hasUserSetupCompleted()) {
235            addAndFinish();
236            return;
237        }
238
239        mAddMsgText = getIntent().getCharSequenceExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION);
240
241        setContentView(R.layout.device_admin_add);
242
243        mAdminIcon = (ImageView)findViewById(R.id.admin_icon);
244        mAdminName = (TextView)findViewById(R.id.admin_name);
245        mAdminDescription = (TextView)findViewById(R.id.admin_description);
246        mProfileOwnerWarning = (TextView) findViewById(R.id.profile_owner_warning);
247
248        mAddMsg = (TextView)findViewById(R.id.add_msg);
249        mAddMsgExpander = (ImageView) findViewById(R.id.add_msg_expander);
250        mAddMsg.setOnClickListener(new View.OnClickListener() {
251            public void onClick(View v) {
252                toggleMessageEllipsis(v);
253            }
254        });
255
256        // toggleMessageEllipsis also handles initial layout:
257        toggleMessageEllipsis(mAddMsg);
258
259        mAdminWarning = (TextView) findViewById(R.id.admin_warning);
260        mAdminPolicies = (ViewGroup) findViewById(R.id.admin_policies);
261        mCancelButton = (Button) findViewById(R.id.cancel_button);
262        mCancelButton.setOnClickListener(new View.OnClickListener() {
263            public void onClick(View v) {
264                EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_DECLINED_BY_USER,
265                    mDeviceAdmin.getActivityInfo().applicationInfo.uid);
266                finish();
267            }
268        });
269        mActionButton = (Button) findViewById(R.id.action_button);
270        mActionButton.setOnClickListener(new View.OnClickListener() {
271            public void onClick(View v) {
272                if (mAdding) {
273                    addAndFinish();
274                } else if (!mWaitingForRemoveMsg) {
275                    try {
276                        // Don't allow the admin to put a dialog up in front
277                        // of us while we interact with the user.
278                        ActivityManagerNative.getDefault().stopAppSwitches();
279                    } catch (RemoteException e) {
280                    }
281                    mWaitingForRemoveMsg = true;
282                    mDPM.getRemoveWarning(mDeviceAdmin.getComponent(),
283                            new RemoteCallback(mHandler) {
284                        @Override
285                        protected void onResult(Bundle bundle) {
286                            CharSequence msg = bundle != null
287                                    ? bundle.getCharSequence(
288                                            DeviceAdminReceiver.EXTRA_DISABLE_WARNING)
289                                    : null;
290                            continueRemoveAction(msg);
291                        }
292                    });
293                    // Don't want to wait too long.
294                    getWindow().getDecorView().getHandler().postDelayed(new Runnable() {
295                        @Override public void run() {
296                            continueRemoveAction(null);
297                        }
298                    }, 2*1000);
299                }
300            }
301        });
302    }
303
304    void addAndFinish() {
305        try {
306            mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing);
307            EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER,
308                mDeviceAdmin.getActivityInfo().applicationInfo.uid);
309            setResult(Activity.RESULT_OK);
310        } catch (RuntimeException e) {
311            // Something bad happened...  could be that it was
312            // already set, though.
313            Log.w(TAG, "Exception trying to activate admin "
314                    + mDeviceAdmin.getComponent(), e);
315            if (mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
316                setResult(Activity.RESULT_OK);
317            }
318        }
319        if (mAddingProfileOwner) {
320            try {
321                mDPM.setProfileOwner(mDeviceAdmin.getComponent(),
322                        mProfileOwnerName, UserHandle.myUserId());
323            } catch (RuntimeException re) {
324                setResult(Activity.RESULT_CANCELED);
325            }
326        }
327        finish();
328    }
329
330    void continueRemoveAction(CharSequence msg) {
331        if (!mWaitingForRemoveMsg) {
332            return;
333        }
334        mWaitingForRemoveMsg = false;
335        if (msg == null) {
336            try {
337                ActivityManagerNative.getDefault().resumeAppSwitches();
338            } catch (RemoteException e) {
339            }
340            mDPM.removeActiveAdmin(mDeviceAdmin.getComponent());
341            finish();
342        } else {
343            try {
344                // Continue preventing anything from coming in front.
345                ActivityManagerNative.getDefault().stopAppSwitches();
346            } catch (RemoteException e) {
347            }
348            Bundle args = new Bundle();
349            args.putCharSequence(
350                    DeviceAdminReceiver.EXTRA_DISABLE_WARNING, msg);
351            showDialog(DIALOG_WARNING, args);
352        }
353    }
354
355    @Override
356    protected void onResume() {
357        super.onResume();
358        updateInterface();
359        // As long as we are running, don't let this admin overlay stuff on top of the screen.
360        final int uid = mDeviceAdmin.getActivityInfo().applicationInfo.uid;
361        final String pkg = mDeviceAdmin.getActivityInfo().applicationInfo.packageName;
362        mCurSysAppOpMode = mAppOps.checkOp(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg);
363        mCurToastAppOpMode = mAppOps.checkOp(AppOpsManager.OP_TOAST_WINDOW, uid, pkg);
364        mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg, AppOpsManager.MODE_IGNORED);
365        mAppOps.setMode(AppOpsManager.OP_TOAST_WINDOW, uid, pkg, AppOpsManager.MODE_IGNORED);
366    }
367
368    @Override
369    protected void onPause() {
370        super.onPause();
371        // As long as we are running, don't let this admin overlay stuff on top of the screen.
372        final int uid = mDeviceAdmin.getActivityInfo().applicationInfo.uid;
373        final String pkg = mDeviceAdmin.getActivityInfo().applicationInfo.packageName;
374        mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg, mCurSysAppOpMode);
375        mAppOps.setMode(AppOpsManager.OP_TOAST_WINDOW, uid, pkg, mCurToastAppOpMode);
376        try {
377            ActivityManagerNative.getDefault().resumeAppSwitches();
378        } catch (RemoteException e) {
379        }
380    }
381
382    @Override
383    protected Dialog onCreateDialog(int id, Bundle args) {
384        switch (id) {
385            case DIALOG_WARNING: {
386                CharSequence msg = args.getCharSequence(DeviceAdminReceiver.EXTRA_DISABLE_WARNING);
387                AlertDialog.Builder builder = new AlertDialog.Builder(
388                        DeviceAdminAdd.this);
389                builder.setMessage(msg);
390                builder.setPositiveButton(R.string.dlg_ok,
391                        new DialogInterface.OnClickListener() {
392                    public void onClick(DialogInterface dialog, int which) {
393                        try {
394                            ActivityManagerNative.getDefault().resumeAppSwitches();
395                        } catch (RemoteException e) {
396                        }
397                        mDPM.removeActiveAdmin(mDeviceAdmin.getComponent());
398                        finish();
399                    }
400                });
401                builder.setNegativeButton(R.string.dlg_cancel, null);
402                return builder.create();
403            }
404            default:
405                return super.onCreateDialog(id, args);
406
407        }
408    }
409
410    static void setViewVisibility(ArrayList<View> views, int visibility) {
411        final int N = views.size();
412        for (int i=0; i<N; i++) {
413            views.get(i).setVisibility(visibility);
414        }
415    }
416
417    void updateInterface() {
418        mAdminIcon.setImageDrawable(mDeviceAdmin.loadIcon(getPackageManager()));
419        mAdminName.setText(mDeviceAdmin.loadLabel(getPackageManager()));
420        try {
421            mAdminDescription.setText(
422                    mDeviceAdmin.loadDescription(getPackageManager()));
423            mAdminDescription.setVisibility(View.VISIBLE);
424        } catch (Resources.NotFoundException e) {
425            mAdminDescription.setVisibility(View.GONE);
426        }
427        if (mAddingProfileOwner) {
428            mProfileOwnerWarning.setVisibility(View.VISIBLE);
429        }
430        if (mAddMsgText != null) {
431            mAddMsg.setText(mAddMsgText);
432            mAddMsg.setVisibility(View.VISIBLE);
433        } else {
434            mAddMsg.setVisibility(View.GONE);
435            mAddMsgExpander.setVisibility(View.GONE);
436        }
437        if (!mRefreshing && !mAddingProfileOwner
438                && mDPM.isAdminActive(mDeviceAdmin.getComponent())) {
439            if (mActivePolicies.size() == 0) {
440                ArrayList<DeviceAdminInfo.PolicyInfo> policies = mDeviceAdmin.getUsedPolicies();
441                for (int i=0; i<policies.size(); i++) {
442                    DeviceAdminInfo.PolicyInfo pi = policies.get(i);
443                    View view = AppSecurityPermissions.getPermissionItemView(
444                            this, getText(pi.label), "", true);
445                    mActivePolicies.add(view);
446                    mAdminPolicies.addView(view);
447                }
448            }
449            setViewVisibility(mActivePolicies, View.VISIBLE);
450            setViewVisibility(mAddingPolicies, View.GONE);
451            mAdminWarning.setText(getString(R.string.device_admin_status,
452                    mDeviceAdmin.getActivityInfo().applicationInfo.loadLabel(getPackageManager())));
453            setTitle(getText(R.string.active_device_admin_msg));
454            mActionButton.setText(getText(R.string.remove_device_admin));
455            mAdding = false;
456        } else {
457            if (mAddingPolicies.size() == 0) {
458                ArrayList<DeviceAdminInfo.PolicyInfo> policies = mDeviceAdmin.getUsedPolicies();
459                for (int i=0; i<policies.size(); i++) {
460                    DeviceAdminInfo.PolicyInfo pi = policies.get(i);
461                    View view = AppSecurityPermissions.getPermissionItemView(
462                            this, getText(pi.label), getText(pi.description), true);
463                    mAddingPolicies.add(view);
464                    mAdminPolicies.addView(view);
465                }
466            }
467            setViewVisibility(mAddingPolicies, View.VISIBLE);
468            setViewVisibility(mActivePolicies, View.GONE);
469            mAdminWarning.setText(getString(R.string.device_admin_warning,
470                    mDeviceAdmin.getActivityInfo().applicationInfo.loadLabel(getPackageManager())));
471            if (mAddingProfileOwner) {
472                setTitle(getText(R.string.profile_owner_add_title));
473            } else {
474                setTitle(getText(R.string.add_device_admin_msg));
475            }
476            mActionButton.setText(getText(R.string.add_device_admin));
477            mAdding = true;
478        }
479    }
480
481
482    void toggleMessageEllipsis(View v) {
483        TextView tv = (TextView) v;
484
485        mAddMsgEllipsized = ! mAddMsgEllipsized;
486        tv.setEllipsize(mAddMsgEllipsized ? TruncateAt.END : null);
487        tv.setMaxLines(mAddMsgEllipsized ? getEllipsizedLines() : MAX_ADD_MSG_LINES);
488
489        mAddMsgExpander.setImageResource(mAddMsgEllipsized ?
490            com.android.internal.R.drawable.expander_ic_minimized :
491            com.android.internal.R.drawable.expander_ic_maximized);
492    }
493
494    int getEllipsizedLines() {
495        Display d = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
496                    .getDefaultDisplay();
497
498        return d.getHeight() > d.getWidth() ?
499            MAX_ADD_MSG_LINES_PORTRAIT : MAX_ADD_MSG_LINES_LANDSCAPE;
500    }
501
502}
503