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