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