1 /* 2 * Copyright (C) 2015 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.packageinstaller.wear; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.FeatureInfo; 24 import android.content.pm.IPackageDeleteObserver; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageParser; 28 import android.database.Cursor; 29 import android.graphics.Bitmap; 30 import android.graphics.drawable.BitmapDrawable; 31 import android.graphics.drawable.Drawable; 32 import android.net.Uri; 33 import android.os.Build; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.IBinder; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.ParcelFileDescriptor; 41 import android.os.PowerManager; 42 import android.os.Process; 43 import android.text.TextUtils; 44 import android.util.Log; 45 46 import com.android.packageinstaller.DeviceUtils; 47 import com.android.packageinstaller.PackageUtil; 48 49 import java.io.ByteArrayOutputStream; 50 import java.io.File; 51 import java.io.FileNotFoundException; 52 import java.io.FileOutputStream; 53 import java.io.IOException; 54 import java.util.ArrayList; 55 import java.util.HashSet; 56 import java.util.List; 57 import java.util.Set; 58 59 /** 60 * Service that will install/uninstall packages. It will check for permissions and features as well. 61 * 62 * ----------- 63 * 64 * Debugging information: 65 * 66 * Install Action example: 67 * adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \ 68 * -d package://com.google.android.gms \ 69 * --eu com.google.android.clockwork.EXTRA_ASSET_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/wearable/com.google.android.gms/apk \ 70 * --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \ 71 * --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \ 72 * --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \ 73 * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService 74 * 75 * Uninstall Action example: 76 * adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \ 77 * -d package://com.google.android.gms \ 78 * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService 79 * 80 * Retry GMS: 81 * adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \ 82 * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService 83 */ 84 public class WearPackageInstallerService extends Service { 85 private static final String TAG = "WearPkgInstallerService"; 86 87 private static final String KEY_PACKAGE_NAME = 88 "com.google.android.clockwork.EXTRA_PACKAGE_NAME"; 89 private static final String KEY_APP_LABEL = "com.google.android.clockwork.EXTRA_APP_LABEL"; 90 private static final String KEY_APP_ICON_URI = 91 "com.google.android.clockwork.EXTRA_APP_ICON_URI"; 92 private static final String KEY_PERMS_LIST = "com.google.android.clockwork.EXTRA_PERMS_LIST"; 93 private static final String KEY_HAS_LAUNCHER = 94 "com.google.android.clockwork.EXTRA_HAS_LAUNCHER"; 95 96 private static final String HOME_APP_PACKAGE_NAME = "com.google.android.wearable.app"; 97 private static final String SHOW_PERMS_SERVICE_CLASS = 98 "com.google.android.clockwork.packagemanager.ShowPermsService"; 99 100 private final int START_INSTALL = 1; 101 private final int START_UNINSTALL = 2; 102 103 private final class ServiceHandler extends Handler { ServiceHandler(Looper looper)104 public ServiceHandler(Looper looper) { 105 super(looper); 106 } 107 handleMessage(Message msg)108 public void handleMessage(Message msg) { 109 switch (msg.what) { 110 case START_INSTALL: 111 installPackage(msg.getData()); 112 break; 113 case START_UNINSTALL: 114 uninstallPackage(msg.getData()); 115 break; 116 } 117 } 118 } 119 private ServiceHandler mServiceHandler; 120 121 private static volatile PowerManager.WakeLock lockStatic = null; 122 123 @Override onBind(Intent intent)124 public IBinder onBind(Intent intent) { 125 return null; 126 } 127 128 @Override onCreate()129 public void onCreate() { 130 super.onCreate(); 131 HandlerThread thread = new HandlerThread("PackageInstallerThread", 132 Process.THREAD_PRIORITY_BACKGROUND); 133 thread.start(); 134 135 mServiceHandler = new ServiceHandler(thread.getLooper()); 136 } 137 138 @Override onStartCommand(Intent intent, int flags, int startId)139 public int onStartCommand(Intent intent, int flags, int startId) { 140 if (!DeviceUtils.isWear(this)) { 141 Log.w(TAG, "Not running on wearable."); 142 return START_NOT_STICKY; 143 } 144 145 if (intent == null) { 146 Log.w(TAG, "Got null intent."); 147 return START_NOT_STICKY; 148 } 149 150 if (Log.isLoggable(TAG, Log.DEBUG)) { 151 Log.d(TAG, "Got install/uninstall request " + intent); 152 } 153 154 Uri packageUri = intent.getData(); 155 if (packageUri == null) { 156 Log.e(TAG, "No package URI in intent"); 157 return START_NOT_STICKY; 158 } 159 final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri); 160 if (packageName == null) { 161 Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri); 162 return START_NOT_STICKY; 163 } 164 165 PowerManager.WakeLock lock = getLock(this.getApplicationContext()); 166 if (!lock.isHeld()) { 167 lock.acquire(); 168 } 169 170 Bundle intentBundle = intent.getExtras(); 171 if (intentBundle == null) { 172 intentBundle = new Bundle(); 173 } 174 WearPackageArgs.setStartId(intentBundle, startId); 175 WearPackageArgs.setPackageName(intentBundle, packageName); 176 if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) { 177 Message msg = mServiceHandler.obtainMessage(START_INSTALL); 178 msg.setData(intentBundle); 179 mServiceHandler.sendMessage(msg); 180 } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) { 181 Message msg = mServiceHandler.obtainMessage(START_UNINSTALL); 182 msg.setData(intentBundle); 183 mServiceHandler.sendMessage(msg); 184 } 185 return START_NOT_STICKY; 186 } 187 installPackage(Bundle argsBundle)188 private void installPackage(Bundle argsBundle) { 189 int startId = WearPackageArgs.getStartId(argsBundle); 190 final String packageName = WearPackageArgs.getPackageName(argsBundle); 191 final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle); 192 final Uri permUri = WearPackageArgs.getPermUri(argsBundle); 193 boolean checkPerms = WearPackageArgs.checkPerms(argsBundle); 194 boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle); 195 int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle); 196 int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle); 197 String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle); 198 boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle); 199 200 if (Log.isLoggable(TAG, Log.DEBUG)) { 201 Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri + 202 ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " + 203 checkPerms + ", skipIfSameVersion: " + skipIfSameVersion + 204 ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " + 205 companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion + 206 ", skipIfLowerVersion: " + skipIfLowerVersion); 207 } 208 final PackageManager pm = getPackageManager(); 209 File tempFile = null; 210 int installFlags = 0; 211 PowerManager.WakeLock lock = getLock(this.getApplicationContext()); 212 boolean messageSent = false; 213 try { 214 PackageInfo existingPkgInfo = null; 215 try { 216 existingPkgInfo = pm.getPackageInfo(packageName, 217 PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS); 218 if(existingPkgInfo != null) { 219 installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 220 } 221 } catch (PackageManager.NameNotFoundException e) { 222 // Ignore this exception. We could not find the package, will treat as a new 223 // installation. 224 } 225 if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) { 226 if (Log.isLoggable(TAG, Log.DEBUG)) { 227 Log.d(TAG, "Replacing package:" + packageName); 228 } 229 } 230 // TODO(28021618): This was left as a temp file due to the fact that this code is being 231 // deprecated and that we need the bare minimum to continue working moving forward 232 // If this code is used as reference, this permission logic might want to be 233 // reworked to use a stream instead of a file so that we don't need to write a 234 // file at all. Note that there might be some trickiness with opening a stream 235 // for multiple users. 236 ParcelFileDescriptor parcelFd = getContentResolver() 237 .openFileDescriptor(assetUri, "r"); 238 tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this, 239 parcelFd, packageName, compressionAlg); 240 if (tempFile == null) { 241 Log.e(TAG, "Could not create a temp file from FD for " + packageName); 242 return; 243 } 244 PackageParser.Package pkg = PackageUtil.getPackageInfo(tempFile); 245 if (pkg == null) { 246 Log.e(TAG, "Could not parse apk information for " + packageName); 247 return; 248 } 249 250 if (!pkg.packageName.equals(packageName)) { 251 Log.e(TAG, "Wearable Package Name has to match what is provided for " + 252 packageName); 253 return; 254 } 255 256 List<String> wearablePerms = pkg.requestedPermissions; 257 258 // Log if the installed pkg has a higher version number. 259 if (existingPkgInfo != null) { 260 if (existingPkgInfo.versionCode == pkg.mVersionCode) { 261 if (skipIfSameVersion) { 262 Log.w(TAG, "Version number (" + pkg.mVersionCode + 263 ") of new app is equal to existing app for " + packageName + 264 "; not installing due to versionCheck"); 265 return; 266 } else { 267 Log.w(TAG, "Version number of new app (" + pkg.mVersionCode + 268 ") is equal to existing app for " + packageName); 269 } 270 } else if (existingPkgInfo.versionCode > pkg.mVersionCode) { 271 if (skipIfLowerVersion) { 272 // Starting in Feldspar, we are not going to allow downgrades of any app. 273 Log.w(TAG, "Version number of new app (" + pkg.mVersionCode + 274 ") is lower than existing app ( " + existingPkgInfo.versionCode + 275 ") for " + packageName + "; not installing due to versionCheck"); 276 return; 277 } else { 278 Log.w(TAG, "Version number of new app (" + pkg.mVersionCode + 279 ") is lower than existing app ( " + existingPkgInfo.versionCode + 280 ") for " + packageName); 281 } 282 } 283 284 // Following the Android Phone model, we should only check for permissions for any 285 // newly defined perms. 286 if (existingPkgInfo.requestedPermissions != null) { 287 for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) { 288 // If the permission is granted, then we will not ask to request it again. 289 if ((existingPkgInfo.requestedPermissionsFlags[i] & 290 PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) { 291 if (Log.isLoggable(TAG, Log.DEBUG)) { 292 Log.d(TAG, existingPkgInfo.requestedPermissions[i] + 293 " is already granted for " + packageName); 294 } 295 wearablePerms.remove(existingPkgInfo.requestedPermissions[i]); 296 } 297 } 298 } 299 } 300 301 // Check permissions on both the new wearable package and also on the already installed 302 // wearable package. 303 // If the app is targeting API level 23, we will also start a service in ClockworkHome 304 // which will ultimately prompt the user to accept/reject permissions. 305 if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion, 306 permUri, wearablePerms, tempFile)) { 307 Log.w(TAG, "Wearable does not have enough permissions."); 308 return; 309 } 310 311 // Check that the wearable has all the features. 312 boolean hasAllFeatures = true; 313 if (pkg.reqFeatures != null) { 314 for (FeatureInfo feature : pkg.reqFeatures) { 315 if (feature.name != null && !pm.hasSystemFeature(feature.name) && 316 (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) { 317 Log.e(TAG, "Wearable does not have required feature: " + feature + 318 " for " + packageName); 319 hasAllFeatures = false; 320 } 321 } 322 } 323 324 if (!hasAllFeatures) { 325 return; 326 } 327 328 // Finally install the package. 329 ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r"); 330 PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd, 331 new PackageInstallListener(this, lock, startId, packageName)); 332 333 messageSent = true; 334 Log.i(TAG, "Sent installation request for " + packageName); 335 } catch (FileNotFoundException e) { 336 Log.e(TAG, "Could not find the file with URI " + assetUri, e); 337 } finally { 338 if (!messageSent) { 339 // Some error happened. If the message has been sent, we can wait for the observer 340 // which will finish the service. 341 if (tempFile != null) { 342 tempFile.delete(); 343 } 344 finishService(lock, startId); 345 } 346 } 347 } 348 349 // TODO: This was left using the old PackageManager API due to the fact that this code is being 350 // deprecated and that we need the bare minimum to continue working moving forward 351 // If this code is used as reference, this logic should be reworked to use the new 352 // PackageInstaller APIs similar to how installPackage was reworked uninstallPackage(Bundle argsBundle)353 private void uninstallPackage(Bundle argsBundle) { 354 int startId = WearPackageArgs.getStartId(argsBundle); 355 final String packageName = WearPackageArgs.getPackageName(argsBundle); 356 357 final PackageManager pm = getPackageManager(); 358 PowerManager.WakeLock lock = getLock(this.getApplicationContext()); 359 pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId), 360 PackageManager.DELETE_ALL_USERS); 361 startPermsServiceForUninstall(packageName); 362 Log.i(TAG, "Sent delete request for " + packageName); 363 } 364 checkPermissions(PackageParser.Package pkg, int companionSdkVersion, int companionDeviceVersion, Uri permUri, List<String> wearablePermissions, File apkFile)365 private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion, 366 int companionDeviceVersion, Uri permUri, List<String> wearablePermissions, 367 File apkFile) { 368 // If the Wear App is targeted for M-release, since the permission model has been changed, 369 // permissions may not be granted on the phone yet. We need a different flow for user to 370 // accept these permissions. 371 // 372 // Assumption: Code is running on E-release, so Wear is always running M. 373 // - Case 1: If the Wear App(WA) is targeting 23, always choose the M model (4 cases) 374 // - Case 2: Else if the Phone App(PA) is targeting 23 and Phone App(P) is running on M, 375 // show a Dialog so that the user can accept all perms (1 case) 376 // - Also show a warning to the developer if the watch is targeting M 377 // - Case 3: If Case 2 is false, then the behavior on the phone is pre-M. Stick to pre-M 378 // behavior on watch (as long as we don't hit case 1). 379 // - 3a: WA(22) PA(22) P(22) -> watch app is not targeting 23 380 // - 3b: WA(22) PA(22) P(23) -> watch app is not targeting 23 381 // - 3c: WA(22) PA(23) P(22) -> watch app is not targeting 23 382 // - Case 4: We did not get Companion App's/Device's version, always show dialog to user to 383 // accept permissions. (This happens if the AndroidWear Companion App is really old). 384 boolean isWearTargetingM = 385 pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; 386 if (isWearTargetingM) { // Case 1 387 // Install the app if Wear App is ready for the new perms model. 388 return true; 389 } 390 391 List<String> unavailableWearablePerms = getWearPermsNotGrantedOnPhone(pkg.packageName, 392 permUri, wearablePermissions); 393 if (unavailableWearablePerms == null) { 394 return false; 395 } 396 397 if (unavailableWearablePerms.size() == 0) { 398 // All permissions requested by the watch are already granted on the phone, no need 399 // to do anything. 400 return true; 401 } 402 403 // Cases 2 and 4. 404 boolean isCompanionTargetingM = companionSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; 405 boolean isCompanionRunningM = companionDeviceVersion > Build.VERSION_CODES.LOLLIPOP_MR1; 406 if (isCompanionTargetingM) { // Case 2 Warning 407 Log.w(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if " + 408 "phone app is targeting at least 23, will continue."); 409 } 410 if ((isCompanionTargetingM && isCompanionRunningM) || // Case 2 411 companionSdkVersion == 0 || companionDeviceVersion == 0) { // Case 4 412 startPermsServiceForInstall(pkg, apkFile, unavailableWearablePerms); 413 } 414 415 // Case 3a-3c. 416 return false; 417 } 418 419 /** 420 * Given a {@string packageName} corresponding to a phone app, query the provider for all the 421 * perms that are granted. 422 * @return null if there is an error retrieving this info 423 * else, a list of all the wearable perms that are not in the list of granted perms of 424 * the phone. 425 */ getWearPermsNotGrantedOnPhone(String packageName, Uri permUri, List<String> wearablePermissions)426 private List<String> getWearPermsNotGrantedOnPhone(String packageName, Uri permUri, 427 List<String> wearablePermissions) { 428 if (permUri == null) { 429 Log.e(TAG, "Permission URI is null"); 430 return null; 431 } 432 Cursor permCursor = getContentResolver().query(permUri, null, null, null, null); 433 if (permCursor == null) { 434 Log.e(TAG, "Could not get the cursor for the permissions"); 435 return null; 436 } 437 438 Set<String> grantedPerms = new HashSet<>(); 439 Set<String> ungrantedPerms = new HashSet<>(); 440 while(permCursor.moveToNext()) { 441 // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and 442 // verify their types. 443 if (permCursor.getColumnCount() == 2 444 && Cursor.FIELD_TYPE_STRING == permCursor.getType(0) 445 && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) { 446 String perm = permCursor.getString(0); 447 Integer granted = permCursor.getInt(1); 448 if (granted == 1) { 449 grantedPerms.add(perm); 450 } else { 451 ungrantedPerms.add(perm); 452 } 453 } 454 } 455 permCursor.close(); 456 457 ArrayList<String> unavailableWearablePerms = new ArrayList<>(); 458 for (String wearablePerm : wearablePermissions) { 459 if (!grantedPerms.contains(wearablePerm)) { 460 unavailableWearablePerms.add(wearablePerm); 461 if (!ungrantedPerms.contains(wearablePerm)) { 462 // This is an error condition. This means that the wearable has permissions that 463 // are not even declared in its host app. This is a developer error. 464 Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm 465 + "\" that is not defined in the host application's manifest."); 466 } else { 467 Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm + 468 "\" that is not granted in the host application."); 469 } 470 } 471 } 472 return unavailableWearablePerms; 473 } 474 finishService(PowerManager.WakeLock lock, int startId)475 private void finishService(PowerManager.WakeLock lock, int startId) { 476 if (lock.isHeld()) { 477 lock.release(); 478 } 479 stopSelf(startId); 480 } 481 getLock(Context context)482 private synchronized PowerManager.WakeLock getLock(Context context) { 483 if (lockStatic == null) { 484 PowerManager mgr = 485 (PowerManager) context.getSystemService(Context.POWER_SERVICE); 486 lockStatic = mgr.newWakeLock( 487 PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName()); 488 lockStatic.setReferenceCounted(true); 489 } 490 return lockStatic; 491 } 492 startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile, List<String> unavailableWearablePerms)493 private void startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile, 494 List<String> unavailableWearablePerms) { 495 final String packageName = pkg.packageName; 496 497 Intent showPermsIntent = new Intent() 498 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS)) 499 .setAction(Intent.ACTION_INSTALL_PACKAGE); 500 final PackageManager pm = getPackageManager(); 501 pkg.applicationInfo.publicSourceDir = apkFile.getPath(); 502 final CharSequence label = pkg.applicationInfo.loadLabel(pm); 503 final Uri iconUri = getIconFileUri(packageName, pkg.applicationInfo.loadIcon(pm)); 504 if (TextUtils.isEmpty(label) || iconUri == null) { 505 Log.e(TAG, "MNC: Could not launch service since either label " + label + 506 ", or icon Uri " + iconUri + " is invalid."); 507 } else { 508 showPermsIntent.putExtra(KEY_APP_LABEL, label); 509 showPermsIntent.putExtra(KEY_APP_ICON_URI, iconUri); 510 showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName); 511 showPermsIntent.putExtra(KEY_PERMS_LIST, 512 unavailableWearablePerms.toArray(new String[0])); 513 showPermsIntent.putExtra(KEY_HAS_LAUNCHER, WearPackageUtil.hasLauncherActivity(pkg)); 514 515 if (Log.isLoggable(TAG, Log.DEBUG)) { 516 Log.d(TAG, "MNC: Launching Intent " + showPermsIntent + " for " + packageName + 517 " with name " + label); 518 } 519 startService(showPermsIntent); 520 } 521 } 522 startPermsServiceForUninstall(final String packageName)523 private void startPermsServiceForUninstall(final String packageName) { 524 Intent showPermsIntent = new Intent() 525 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS)) 526 .setAction(Intent.ACTION_UNINSTALL_PACKAGE); 527 showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName); 528 if (Log.isLoggable(TAG, Log.DEBUG)) { 529 Log.d(TAG, "Launching Intent " + showPermsIntent + " for " + packageName); 530 } 531 startService(showPermsIntent); 532 } 533 getIconFileUri(final String packageName, final Drawable d)534 private Uri getIconFileUri(final String packageName, final Drawable d) { 535 if (d == null || !(d instanceof BitmapDrawable)) { 536 Log.e(TAG, "Drawable is not a BitmapDrawable for " + packageName); 537 return null; 538 } 539 File iconFile = WearPackageUtil.getIconFile(this, packageName); 540 541 if (iconFile == null) { 542 Log.e(TAG, "Could not get icon file for " + packageName); 543 return null; 544 } 545 546 FileOutputStream fos = null; 547 try { 548 // Convert bitmap to byte array 549 Bitmap bitmap = ((BitmapDrawable) d).getBitmap(); 550 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 551 bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); 552 553 // Write the bytes into the file 554 fos = new FileOutputStream(iconFile); 555 fos.write(bos.toByteArray()); 556 fos.flush(); 557 558 return WearPackageIconProvider.getUriForPackage(packageName); 559 } catch (IOException e) { 560 Log.e(TAG, "Could not convert drawable to icon file for package " + packageName, e); 561 return null; 562 } finally { 563 if (fos != null) { 564 try { 565 fos.close(); 566 } catch (IOException e) { 567 // ignore 568 } 569 } 570 } 571 } 572 573 private class PackageInstallListener implements PackageInstallerImpl.InstallListener { 574 private Context mContext; 575 private PowerManager.WakeLock mWakeLock; 576 private int mStartId; 577 private String mApplicationPackageName; PackageInstallListener(Context context, PowerManager.WakeLock wakeLock, int startId, String applicationPackageName)578 private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock, 579 int startId, String applicationPackageName) { 580 mContext = context; 581 mWakeLock = wakeLock; 582 mStartId = startId; 583 mApplicationPackageName = applicationPackageName; 584 } 585 586 @Override installBeginning()587 public void installBeginning() { 588 Log.i(TAG, "Package " + mApplicationPackageName + " is being installed."); 589 } 590 591 @Override installSucceeded()592 public void installSucceeded() { 593 try { 594 Log.i(TAG, "Package " + mApplicationPackageName + " was installed."); 595 596 // Delete tempFile from the file system. 597 File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName); 598 if (tempFile != null) { 599 tempFile.delete(); 600 } 601 } finally { 602 finishService(mWakeLock, mStartId); 603 } 604 } 605 606 @Override installFailed(int errorCode, String errorDesc)607 public void installFailed(int errorCode, String errorDesc) { 608 Log.e(TAG, "Package install failed " + mApplicationPackageName 609 + ", errorCode " + errorCode); 610 WearPackageUtil.removeFromPermStore(mContext, mApplicationPackageName); 611 finishService(mWakeLock, mStartId); 612 } 613 } 614 615 private class PackageDeleteObserver extends IPackageDeleteObserver.Stub { 616 private PowerManager.WakeLock mWakeLock; 617 private int mStartId; 618 PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId)619 private PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId) { 620 mWakeLock = wakeLock; 621 mStartId = startId; 622 } 623 packageDeleted(String packageName, int returnCode)624 public void packageDeleted(String packageName, int returnCode) { 625 try { 626 if (returnCode >= 0) { 627 Log.i(TAG, "Package " + packageName + " was uninstalled."); 628 } else { 629 Log.e(TAG, "Package uninstall failed " + packageName + ", returnCode " + 630 returnCode); 631 } 632 } finally { 633 finishService(mWakeLock, mStartId); 634 } 635 } 636 } 637 } 638