1 /* 2 * Copyright (C) 2023 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.server.healthconnect.migration; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.health.connect.Constants; 27 import android.health.connect.HealthConnectManager; 28 import android.util.Slog; 29 30 import libcore.util.HexEncoding; 31 32 import java.security.MessageDigest; 33 import java.security.NoSuchAlgorithmException; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.stream.Collectors; 37 38 /** @hide */ 39 public class MigrationUtils { 40 private static final String TAG = "HealthConnectMigrationUtils"; 41 42 /** 43 * Filters and returns the package names of applications which hold permission {@link 44 * android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA}. 45 * 46 * @return List of filtered app package names which hold the specified permission 47 */ 48 @NonNull filterPermissions(@onNull Context context)49 public static List<String> filterPermissions(@NonNull Context context) { 50 if (android.health.connect.Constants.DEBUG) { 51 Slog.d(TAG, "Calling filterPermissions()"); 52 } 53 54 String[] permissions = new String[] {Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA}; 55 56 List<PackageInfo> packageInfos = 57 context.getPackageManager() 58 .getPackagesHoldingPermissions( 59 permissions, PackageManager.PackageInfoFlags.of(0)); 60 61 List<String> permissionFilteredPackages = 62 packageInfos.stream().map(info -> info.packageName).collect(Collectors.toList()); 63 64 if (android.health.connect.Constants.DEBUG) { 65 Slog.d(TAG, "permissionFilteredPackages : " + permissionFilteredPackages); 66 } 67 return permissionFilteredPackages; 68 } 69 70 /** 71 * Filters and returns the package names of applications which handle intent {@link 72 * android.health.connect.HealthConnectManager#ACTION_SHOW_MIGRATION_INFO}. 73 * 74 * @param permissionFilteredPackages List of app package names holding permission {@link 75 * android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA} 76 * @return List of filtered app package names which handle the specified intent action 77 */ 78 @NonNull filterIntent( @onNull Context context, @NonNull List<String> permissionFilteredPackages)79 public static List<String> filterIntent( 80 @NonNull Context context, @NonNull List<String> permissionFilteredPackages) { 81 return filterIntent(context, permissionFilteredPackages, PackageManager.MATCH_ALL); 82 } 83 84 /** 85 * Filters and returns the package names of applications which handle intent {@link 86 * android.health.connect.HealthConnectManager#ACTION_SHOW_MIGRATION_INFO}. 87 * 88 * @param permissionFilteredPackages List of app package names holding permission {@link 89 * android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA} 90 * @param flags Additional option flags to modify the data returned. 91 * @return List of filtered app package names which handle the specified intent action 92 */ 93 @NonNull filterIntent( @onNull Context context, @NonNull List<String> permissionFilteredPackages, int flags)94 public static List<String> filterIntent( 95 @NonNull Context context, @NonNull List<String> permissionFilteredPackages, int flags) { 96 if (android.health.connect.Constants.DEBUG) { 97 Slog.d(TAG, "Calling filterIntents()"); 98 } 99 100 List<String> filteredPackages = new ArrayList<String>(permissionFilteredPackages.size()); 101 102 for (String packageName : permissionFilteredPackages) { 103 104 if (android.health.connect.Constants.DEBUG) { 105 Slog.d(TAG, "Checking intent for package : " + packageName); 106 } 107 108 Intent intentToCheck = 109 new Intent(HealthConnectManager.ACTION_SHOW_MIGRATION_INFO) 110 .setPackage(packageName); 111 112 ResolveInfo resolveResult = 113 context.getPackageManager() 114 .resolveActivity( 115 intentToCheck, PackageManager.ResolveInfoFlags.of(flags)); 116 117 if (resolveResult != null) { 118 filteredPackages.add(packageName); 119 } 120 } 121 if (Constants.DEBUG) { 122 Slog.d(TAG, "filteredPackages : " + filteredPackages); 123 } 124 return filteredPackages; 125 } 126 127 /** Computes the SHA256 digest of the input data. */ 128 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression computeSha256DigestBytes(@onNull byte[] data)129 public static String computeSha256DigestBytes(@NonNull byte[] data) { 130 MessageDigest messageDigest; 131 try { 132 messageDigest = MessageDigest.getInstance("SHA256"); 133 } catch (NoSuchAlgorithmException e) { 134 return null; 135 } 136 messageDigest.update(data); 137 138 return HexEncoding.encodeToString(messageDigest.digest(), /* uppercase= */ true); 139 } 140 141 /** Checks if the package is stub by checking if its installer source is not set. */ isPackageStub(Context context, String packageName)142 public static boolean isPackageStub(Context context, String packageName) { 143 try { 144 return context.getPackageManager() 145 .getInstallSourceInfo(packageName) 146 .getInstallingPackageName() 147 == null; 148 } catch (PackageManager.NameNotFoundException e) { 149 Slog.w(TAG, "Package not found " + packageName); 150 } 151 return false; 152 } 153 } 154