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 android.app.admin; 18 19 import android.annotation.NonNull; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.pm.ActivityInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.content.pm.ResolveInfo; 27 import android.content.res.Resources; 28 import android.content.res.Resources.NotFoundException; 29 import android.content.res.TypedArray; 30 import android.content.res.XmlResourceParser; 31 import android.graphics.drawable.Drawable; 32 import android.os.Build; 33 import android.os.Parcel; 34 import android.os.Parcelable; 35 import android.os.PersistableBundle; 36 import android.util.AttributeSet; 37 import android.util.Log; 38 import android.util.Printer; 39 import android.util.SparseArray; 40 import android.util.Xml; 41 42 import org.xmlpull.v1.XmlPullParser; 43 import org.xmlpull.v1.XmlPullParserException; 44 import org.xmlpull.v1.XmlSerializer; 45 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.HashMap; 49 50 /** 51 * This class is used to specify meta information of a device administrator 52 * component. 53 */ 54 public final class DeviceAdminInfo implements Parcelable { 55 static final String TAG = "DeviceAdminInfo"; 56 57 /** 58 * A type of policy that this device admin can use: profile owner on an organization-owned 59 * device. 60 * 61 * @hide 62 */ 63 public static final int USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER = -3; 64 65 /** 66 * A type of policy that this device admin can use: device owner meta-policy 67 * for an admin that is designated as owner of the device. 68 * 69 * @hide 70 */ 71 public static final int USES_POLICY_DEVICE_OWNER = -2; 72 73 /** 74 * A type of policy that this device admin can use: profile owner meta-policy 75 * for admins that have been installed as owner of some user profile. 76 * 77 * @hide 78 */ 79 public static final int USES_POLICY_PROFILE_OWNER = -1; 80 81 /** 82 * A type of policy that this device admin can use: limit the passwords 83 * that the user can select, via {@link DevicePolicyManager#setPasswordQuality} 84 * and {@link DevicePolicyManager#setPasswordMinimumLength}. 85 * 86 * <p>To control this policy, the device admin must be a device owner or profile owner, 87 * and must have a "limit-password" tag in the "uses-policies" section of its meta-data. 88 * If used by a device owner, the policy only affects the primary user and its profiles, 89 * but not any secondary users on the device. 90 */ 91 public static final int USES_POLICY_LIMIT_PASSWORD = 0; 92 93 /** 94 * A type of policy that this device admin can use: able to watch login 95 * attempts from the user, via {@link DeviceAdminReceiver#ACTION_PASSWORD_FAILED}, 96 * {@link DeviceAdminReceiver#ACTION_PASSWORD_SUCCEEDED}, and 97 * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}. 98 * 99 * <p>To control this policy, the device admin must have a "watch-login" 100 * tag in the "uses-policies" section of its meta-data. 101 */ 102 public static final int USES_POLICY_WATCH_LOGIN = 1; 103 104 /** 105 * A type of policy that this device admin can use: able to reset the 106 * user's password via 107 * {@link DevicePolicyManager#resetPassword}. 108 * 109 * <p>To control this policy, the device admin must have a "reset-password" 110 * tag in the "uses-policies" section of its meta-data. 111 */ 112 public static final int USES_POLICY_RESET_PASSWORD = 2; 113 114 /** 115 * A type of policy that this device admin can use: able to force the device 116 * to lock via{@link DevicePolicyManager#lockNow} or limit the 117 * maximum lock timeout for the device via 118 * {@link DevicePolicyManager#setMaximumTimeToLock}. 119 * 120 * <p>To control this policy, the device admin must have a "force-lock" 121 * tag in the "uses-policies" section of its meta-data. 122 */ 123 public static final int USES_POLICY_FORCE_LOCK = 3; 124 125 /** 126 * A type of policy that this device admin can use: able to factory 127 * reset the device, erasing all of the user's data, via 128 * {@link DevicePolicyManager#wipeData}. 129 * 130 * <p>To control this policy, the device admin must have a "wipe-data" 131 * tag in the "uses-policies" section of its meta-data. 132 */ 133 public static final int USES_POLICY_WIPE_DATA = 4; 134 135 /** 136 * A type of policy that this device admin can use: able to specify the 137 * device Global Proxy, via {@link DevicePolicyManager#setGlobalProxy}. 138 * 139 * <p>To control this policy, the device admin must have a "set-global-proxy" 140 * tag in the "uses-policies" section of its meta-data. 141 * @hide 142 */ 143 public static final int USES_POLICY_SETS_GLOBAL_PROXY = 5; 144 145 /** 146 * A type of policy that this device admin can use: force the user to 147 * change their password after an administrator-defined time limit. 148 * 149 * <p>To control this policy, the device admin must be a device owner or profile owner, 150 * and must have an "expire-password" tag in the "uses-policies" section of its meta-data. 151 * If used by a device owner, the policy only affects the primary user and its profiles, 152 * but not any secondary users on the device. 153 */ 154 public static final int USES_POLICY_EXPIRE_PASSWORD = 6; 155 156 /** 157 * A type of policy that this device admin can use: require encryption of stored data. 158 * 159 * <p>To control this policy, the device admin must have a "encrypted-storage" 160 * tag in the "uses-policies" section of its meta-data. 161 */ 162 public static final int USES_ENCRYPTED_STORAGE = 7; 163 164 /** 165 * A type of policy that this device admin can use: disables use of all device cameras. 166 * 167 * <p>To control this policy, the device admin must be a device owner or profile owner, 168 * and must have a "disable-camera" tag in the "uses-policies" section of its meta-data. 169 * If used by a device owner, the policy affects all users on the device. 170 */ 171 public static final int USES_POLICY_DISABLE_CAMERA = 8; 172 173 /** 174 * A type of policy that this device admin can use: disables use of keyguard features. 175 * 176 * <p>To control this policy, the device admin must be a device owner or profile owner, 177 * and must have a "disable-keyguard-features" tag in the "uses-policies" section of its 178 * meta-data. If used by a device owner, the policy only affects the primary user and 179 * its profiles, but not any secondary users on the device. 180 */ 181 public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9; 182 183 /** @hide */ 184 public static class PolicyInfo { 185 public final int ident; 186 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 187 public final String tag; 188 public final int label; 189 public final int description; 190 public final int labelForSecondaryUsers; 191 public final int descriptionForSecondaryUsers; 192 PolicyInfo(int ident, String tag, int label, int description)193 public PolicyInfo(int ident, String tag, int label, int description) { 194 this(ident, tag, label, description, label, description); 195 } 196 PolicyInfo(int ident, String tag, int label, int description, int labelForSecondaryUsers, int descriptionForSecondaryUsers)197 public PolicyInfo(int ident, String tag, int label, int description, 198 int labelForSecondaryUsers, int descriptionForSecondaryUsers) { 199 this.ident = ident; 200 this.tag = tag; 201 this.label = label; 202 this.description = description; 203 this.labelForSecondaryUsers = labelForSecondaryUsers; 204 this.descriptionForSecondaryUsers = descriptionForSecondaryUsers; 205 } 206 } 207 208 static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>(); 209 static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>(); 210 static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>(); 211 212 static { sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data", com.android.internal.R.string.policylab_wipeData, com.android.internal.R.string.policydesc_wipeData, com.android.internal.R.string.policylab_wipeData_secondaryUser, com.android.internal.R.string.policydesc_wipeData_secondaryUser ))213 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data", 214 com.android.internal.R.string.policylab_wipeData, 215 com.android.internal.R.string.policydesc_wipeData, 216 com.android.internal.R.string.policylab_wipeData_secondaryUser, 217 com.android.internal.R.string.policydesc_wipeData_secondaryUser 218 )); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_RESET_PASSWORD, "reset-password", com.android.internal.R.string.policylab_resetPassword, com.android.internal.R.string.policydesc_resetPassword))219 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_RESET_PASSWORD, "reset-password", 220 com.android.internal.R.string.policylab_resetPassword, 221 com.android.internal.R.string.policydesc_resetPassword)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_PASSWORD, "limit-password", com.android.internal.R.string.policylab_limitPassword, com.android.internal.R.string.policydesc_limitPassword))222 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_PASSWORD, "limit-password", 223 com.android.internal.R.string.policylab_limitPassword, 224 com.android.internal.R.string.policydesc_limitPassword)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WATCH_LOGIN, "watch-login", com.android.internal.R.string.policylab_watchLogin, com.android.internal.R.string.policydesc_watchLogin, com.android.internal.R.string.policylab_watchLogin, com.android.internal.R.string.policydesc_watchLogin_secondaryUser ))225 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WATCH_LOGIN, "watch-login", 226 com.android.internal.R.string.policylab_watchLogin, 227 com.android.internal.R.string.policydesc_watchLogin, 228 com.android.internal.R.string.policylab_watchLogin, 229 com.android.internal.R.string.policydesc_watchLogin_secondaryUser 230 )); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock", com.android.internal.R.string.policylab_forceLock, com.android.internal.R.string.policydesc_forceLock))231 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock", 232 com.android.internal.R.string.policylab_forceLock, 233 com.android.internal.R.string.policydesc_forceLock)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_SETS_GLOBAL_PROXY, "set-global-proxy", com.android.internal.R.string.policylab_setGlobalProxy, com.android.internal.R.string.policydesc_setGlobalProxy))234 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_SETS_GLOBAL_PROXY, "set-global-proxy", 235 com.android.internal.R.string.policylab_setGlobalProxy, 236 com.android.internal.R.string.policydesc_setGlobalProxy)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_EXPIRE_PASSWORD, "expire-password", com.android.internal.R.string.policylab_expirePassword, com.android.internal.R.string.policydesc_expirePassword))237 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_EXPIRE_PASSWORD, "expire-password", 238 com.android.internal.R.string.policylab_expirePassword, 239 com.android.internal.R.string.policydesc_expirePassword)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_ENCRYPTED_STORAGE, "encrypted-storage", com.android.internal.R.string.policylab_encryptedStorage, com.android.internal.R.string.policydesc_encryptedStorage))240 sPoliciesDisplayOrder.add(new PolicyInfo(USES_ENCRYPTED_STORAGE, "encrypted-storage", 241 com.android.internal.R.string.policylab_encryptedStorage, 242 com.android.internal.R.string.policydesc_encryptedStorage)); sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera", com.android.internal.R.string.policylab_disableCamera, com.android.internal.R.string.policydesc_disableCamera))243 sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera", 244 com.android.internal.R.string.policylab_disableCamera, 245 com.android.internal.R.string.policydesc_disableCamera)); sPoliciesDisplayOrder.add(new PolicyInfo( USES_POLICY_DISABLE_KEYGUARD_FEATURES, "disable-keyguard-features", com.android.internal.R.string.policylab_disableKeyguardFeatures, com.android.internal.R.string.policydesc_disableKeyguardFeatures))246 sPoliciesDisplayOrder.add(new PolicyInfo( 247 USES_POLICY_DISABLE_KEYGUARD_FEATURES, "disable-keyguard-features", 248 com.android.internal.R.string.policylab_disableKeyguardFeatures, 249 com.android.internal.R.string.policydesc_disableKeyguardFeatures)); 250 251 for (int i=0; i<sPoliciesDisplayOrder.size(); i++) { 252 PolicyInfo pi = sPoliciesDisplayOrder.get(i); sRevKnownPolicies.put(pi.ident, pi)253 sRevKnownPolicies.put(pi.ident, pi); sKnownPolicies.put(pi.tag, pi.ident)254 sKnownPolicies.put(pi.tag, pi.ident); 255 } 256 } 257 258 /** 259 * The BroadcastReceiver that implements this device admin component. 260 */ 261 final ActivityInfo mActivityInfo; 262 263 /** 264 * Whether this should be visible to the user. 265 */ 266 boolean mVisible; 267 268 /** 269 * The policies this administrator needs access to. 270 */ 271 int mUsesPolicies; 272 273 /** 274 * Whether this administrator can be a target in an ownership transfer. 275 * 276 * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle) 277 */ 278 boolean mSupportsTransferOwnership; 279 280 /** 281 * Constructor. 282 * 283 * @param context The Context in which we are parsing the device admin. 284 * @param resolveInfo The ResolveInfo returned from the package manager about 285 * this device admin's component. 286 */ DeviceAdminInfo(Context context, ResolveInfo resolveInfo)287 public DeviceAdminInfo(Context context, ResolveInfo resolveInfo) 288 throws XmlPullParserException, IOException { 289 this(context, resolveInfo.activityInfo); 290 } 291 /** 292 * Constructor. 293 * 294 * @param context The Context in which we are parsing the device admin. 295 * @param activityInfo The ActivityInfo returned from the package manager about 296 * this device admin's component. 297 * 298 * @hide 299 */ DeviceAdminInfo(Context context, ActivityInfo activityInfo)300 public DeviceAdminInfo(Context context, ActivityInfo activityInfo) 301 throws XmlPullParserException, IOException { 302 mActivityInfo = activityInfo; 303 304 PackageManager pm = context.getPackageManager(); 305 306 XmlResourceParser parser = null; 307 try { 308 parser = mActivityInfo.loadXmlMetaData(pm, DeviceAdminReceiver.DEVICE_ADMIN_META_DATA); 309 if (parser == null) { 310 throw new XmlPullParserException("No " 311 + DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data"); 312 } 313 314 Resources res = pm.getResourcesForApplication(mActivityInfo.applicationInfo); 315 316 AttributeSet attrs = Xml.asAttributeSet(parser); 317 318 int type; 319 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 320 && type != XmlPullParser.START_TAG) { 321 } 322 323 String nodeName = parser.getName(); 324 if (!"device-admin".equals(nodeName)) { 325 throw new XmlPullParserException( 326 "Meta-data does not start with device-admin tag"); 327 } 328 329 TypedArray sa = res.obtainAttributes(attrs, 330 com.android.internal.R.styleable.DeviceAdmin); 331 332 mVisible = sa.getBoolean( 333 com.android.internal.R.styleable.DeviceAdmin_visible, true); 334 335 sa.recycle(); 336 337 int outerDepth = parser.getDepth(); 338 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 339 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 340 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 341 continue; 342 } 343 String tagName = parser.getName(); 344 if (tagName.equals("uses-policies")) { 345 int innerDepth = parser.getDepth(); 346 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 347 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 348 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 349 continue; 350 } 351 String policyName = parser.getName(); 352 Integer val = sKnownPolicies.get(policyName); 353 if (val != null) { 354 mUsesPolicies |= 1 << val.intValue(); 355 } else { 356 Log.w(TAG, "Unknown tag under uses-policies of " 357 + getComponent() + ": " + policyName); 358 } 359 } 360 } else if (tagName.equals("support-transfer-ownership")) { 361 if (parser.next() != XmlPullParser.END_TAG) { 362 throw new XmlPullParserException( 363 "support-transfer-ownership tag must be empty."); 364 } 365 mSupportsTransferOwnership = true; 366 } 367 } 368 } catch (NameNotFoundException e) { 369 throw new XmlPullParserException( 370 "Unable to create context for: " + mActivityInfo.packageName); 371 } finally { 372 if (parser != null) parser.close(); 373 } 374 } 375 DeviceAdminInfo(Parcel source)376 DeviceAdminInfo(Parcel source) { 377 mActivityInfo = ActivityInfo.CREATOR.createFromParcel(source); 378 mUsesPolicies = source.readInt(); 379 mSupportsTransferOwnership = source.readBoolean(); 380 } 381 382 /** 383 * Return the .apk package that implements this device admin. 384 */ getPackageName()385 public String getPackageName() { 386 return mActivityInfo.packageName; 387 } 388 389 /** 390 * Return the class name of the receiver component that implements 391 * this device admin. 392 */ getReceiverName()393 public String getReceiverName() { 394 return mActivityInfo.name; 395 } 396 397 /** 398 * Return the raw information about the receiver implementing this 399 * device admin. Do not modify the returned object. 400 */ getActivityInfo()401 public ActivityInfo getActivityInfo() { 402 return mActivityInfo; 403 } 404 405 /** 406 * Return the component of the receiver that implements this device admin. 407 */ 408 @NonNull getComponent()409 public ComponentName getComponent() { 410 return new ComponentName(mActivityInfo.packageName, 411 mActivityInfo.name); 412 } 413 414 /** 415 * Load the user-displayed label for this device admin. 416 * 417 * @param pm Supply a PackageManager used to load the device admin's 418 * resources. 419 */ loadLabel(PackageManager pm)420 public CharSequence loadLabel(PackageManager pm) { 421 return mActivityInfo.loadLabel(pm); 422 } 423 424 /** 425 * Load user-visible description associated with this device admin. 426 * 427 * @param pm Supply a PackageManager used to load the device admin's 428 * resources. 429 */ loadDescription(PackageManager pm)430 public CharSequence loadDescription(PackageManager pm) throws NotFoundException { 431 if (mActivityInfo.descriptionRes != 0) { 432 return pm.getText(mActivityInfo.packageName, 433 mActivityInfo.descriptionRes, mActivityInfo.applicationInfo); 434 } 435 throw new NotFoundException(); 436 } 437 438 /** 439 * Load the user-displayed icon for this device admin. 440 * 441 * @param pm Supply a PackageManager used to load the device admin's 442 * resources. 443 */ loadIcon(PackageManager pm)444 public Drawable loadIcon(PackageManager pm) { 445 return mActivityInfo.loadIcon(pm); 446 } 447 448 /** 449 * Returns whether this device admin would like to be visible to the 450 * user, even when it is not enabled. 451 */ isVisible()452 public boolean isVisible() { 453 return mVisible; 454 } 455 456 /** 457 * Return true if the device admin has requested that it be able to use 458 * the given policy control. The possible policy identifier inputs are: 459 * {@link #USES_POLICY_LIMIT_PASSWORD}, {@link #USES_POLICY_WATCH_LOGIN}, 460 * {@link #USES_POLICY_RESET_PASSWORD}, {@link #USES_POLICY_FORCE_LOCK}, 461 * {@link #USES_POLICY_WIPE_DATA}, 462 * {@link #USES_POLICY_EXPIRE_PASSWORD}, {@link #USES_ENCRYPTED_STORAGE}, 463 * {@link #USES_POLICY_DISABLE_CAMERA}. 464 */ usesPolicy(int policyIdent)465 public boolean usesPolicy(int policyIdent) { 466 return (mUsesPolicies & (1<<policyIdent)) != 0; 467 } 468 469 /** 470 * Return the XML tag name for the given policy identifier. Valid identifiers 471 * are as per {@link #usesPolicy(int)}. If the given identifier is not 472 * known, null is returned. 473 */ getTagForPolicy(int policyIdent)474 public String getTagForPolicy(int policyIdent) { 475 return sRevKnownPolicies.get(policyIdent).tag; 476 } 477 478 /** 479 * Return true if this administrator can be a target in an ownership transfer. 480 */ supportsTransferOwnership()481 public boolean supportsTransferOwnership() { 482 return mSupportsTransferOwnership; 483 } 484 485 /** @hide */ 486 @UnsupportedAppUsage getUsedPolicies()487 public ArrayList<PolicyInfo> getUsedPolicies() { 488 ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>(); 489 for (int i=0; i<sPoliciesDisplayOrder.size(); i++) { 490 PolicyInfo pi = sPoliciesDisplayOrder.get(i); 491 if (usesPolicy(pi.ident)) { 492 res.add(pi); 493 } 494 } 495 return res; 496 } 497 498 /** @hide */ writePoliciesToXml(XmlSerializer out)499 public void writePoliciesToXml(XmlSerializer out) 500 throws IllegalArgumentException, IllegalStateException, IOException { 501 out.attribute(null, "flags", Integer.toString(mUsesPolicies)); 502 } 503 504 /** @hide */ readPoliciesFromXml(XmlPullParser parser)505 public void readPoliciesFromXml(XmlPullParser parser) 506 throws XmlPullParserException, IOException { 507 mUsesPolicies = Integer.parseInt( 508 parser.getAttributeValue(null, "flags")); 509 } 510 dump(Printer pw, String prefix)511 public void dump(Printer pw, String prefix) { 512 pw.println(prefix + "Receiver:"); 513 mActivityInfo.dump(pw, prefix + " "); 514 } 515 516 @Override toString()517 public String toString() { 518 return "DeviceAdminInfo{" + mActivityInfo.name + "}"; 519 } 520 521 /** 522 * Used to package this object into a {@link Parcel}. 523 * 524 * @param dest The {@link Parcel} to be written. 525 * @param flags The flags used for parceling. 526 */ writeToParcel(Parcel dest, int flags)527 public void writeToParcel(Parcel dest, int flags) { 528 mActivityInfo.writeToParcel(dest, flags); 529 dest.writeInt(mUsesPolicies); 530 dest.writeBoolean(mSupportsTransferOwnership); 531 } 532 533 /** 534 * Used to make this class parcelable. 535 */ 536 public static final @android.annotation.NonNull Parcelable.Creator<DeviceAdminInfo> CREATOR = 537 new Parcelable.Creator<DeviceAdminInfo>() { 538 public DeviceAdminInfo createFromParcel(Parcel source) { 539 return new DeviceAdminInfo(source); 540 } 541 542 public DeviceAdminInfo[] newArray(int size) { 543 return new DeviceAdminInfo[size]; 544 } 545 }; 546 describeContents()547 public int describeContents() { 548 return 0; 549 } 550 } 551