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 package com.android.car.pm; 17 18 import static android.car.content.pm.CarPackageManager.DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.UserIdInt; 23 import android.car.content.pm.CarPackageManager; 24 import android.content.Context; 25 import android.content.pm.ActivityInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.os.Bundle; 30 import android.util.Slog; 31 32 import com.android.car.CarLog; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.List; 38 39 /** 40 * Read App meta data and look for Distraction Optimization(DO) tags. 41 * An app can tag a distraction optimized activity to be DO by adding the following meta-data 42 * to that <activity> element: 43 * 44 * <pre>{@code 45 * <activity> 46 * <meta-data android:name="distractionOptimized" android:value="true"/> 47 * </activity> 48 * }</pre> 49 * 50 */ 51 public class CarAppMetadataReader { 52 53 private static final String TAG = CarLog.tagFor(CarAppMetadataReader.class); 54 55 /** Name of the meta-data attribute of the Activity that denotes distraction optimized */ 56 private static final String DO_METADATA_ATTRIBUTE = "distractionOptimized"; 57 58 private static final List<String> ALL_REGION_ONLY = Collections.singletonList( 59 CarPackageManager.DRIVING_SAFETY_REGION_ALL); 60 61 @Nullable getAllActivitiesForPackageAsUser(Context context, String packageName, @UserIdInt int userId)62 private static ActivityInfo[] getAllActivitiesForPackageAsUser(Context context, 63 String packageName, @UserIdInt int userId) throws NameNotFoundException { 64 final PackageManager pm = context.getPackageManager(); 65 PackageInfo pkgInfo = 66 pm.getPackageInfoAsUser( 67 packageName, PackageManager.GET_ACTIVITIES 68 | PackageManager.GET_META_DATA 69 | PackageManager.MATCH_DISABLED_COMPONENTS 70 | PackageManager.MATCH_DIRECT_BOOT_AWARE 71 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 72 userId); 73 if (pkgInfo == null) { 74 return null; 75 } 76 77 return pkgInfo.activities; 78 } 79 80 /** 81 * Parses the given package and returns Distraction Optimized information, if present. 82 * 83 * @return Array of DO activity names in the given package 84 */ 85 @Nullable findDistractionOptimizedActivitiesAsUser(Context context, String packageName, @UserIdInt int userId, @NonNull String drivingSafetyRegion)86 public static String[] findDistractionOptimizedActivitiesAsUser(Context context, 87 String packageName, @UserIdInt int userId, @NonNull String drivingSafetyRegion) 88 throws NameNotFoundException { 89 90 91 // Check if any of the activities in the package are DO by checking all the 92 // <activity> elements. MATCH_DISABLED_COMPONENTS is included so that we are immediately 93 // prepared to respond to any components that toggle from disabled to enabled. 94 ActivityInfo[] activities = getAllActivitiesForPackageAsUser(context, packageName, userId); 95 if (activities == null) { 96 if (CarPackageManagerService.DBG) { 97 Slog.d(TAG, "Null Activities for " + packageName); 98 } 99 return null; 100 } 101 List<String> optimizedActivityList = new ArrayList(); 102 for (ActivityInfo activity : activities) { 103 Bundle metaData = activity.metaData; 104 if (metaData == null) { 105 continue; 106 } 107 String regionString = metaData.getString(DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS, 108 CarPackageManager.DRIVING_SAFETY_REGION_ALL); 109 if (!isRegionSupported(regionString, drivingSafetyRegion)) { 110 continue; 111 } 112 if (metaData.getBoolean(DO_METADATA_ATTRIBUTE, false)) { 113 if (CarPackageManagerService.DBG) { 114 Slog.d(TAG, 115 "DO Activity:" + activity.packageName + "/" + activity.name); 116 } 117 optimizedActivityList.add(activity.name); 118 } 119 } 120 if (optimizedActivityList.isEmpty()) { 121 return null; 122 } 123 return optimizedActivityList.toArray(new String[optimizedActivityList.size()]); 124 } 125 126 /** 127 * Check {@link CarPackageManager#getSupportedDrivingSafetyRegionsForActivityAsUser( 128 * String, String, int)}. 129 */ getSupportedDrivingSafetyRegionsForActivityAsUser(Context context, String packageName, String activityClassName, @UserIdInt int userId)130 public static List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(Context context, 131 String packageName, String activityClassName, @UserIdInt int userId) 132 throws NameNotFoundException { 133 ActivityInfo[] activities = getAllActivitiesForPackageAsUser(context, packageName, userId); 134 if (activities == null) { 135 throw new NameNotFoundException(); 136 } 137 for (ActivityInfo info: activities) { 138 if (!info.name.equals(activityClassName)) { 139 continue; 140 } 141 // Found activity 142 Bundle metaData = info.metaData; 143 if (metaData == null) { 144 return Collections.EMPTY_LIST; 145 } 146 if (!metaData.getBoolean(DO_METADATA_ATTRIBUTE, false)) { 147 // no distractionOptimized, so region metadata does not matter. 148 return Collections.EMPTY_LIST; 149 } 150 String regionString = metaData.getString(DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS, 151 CarPackageManager.DRIVING_SAFETY_REGION_ALL); 152 String[] regions = regionString.split(","); 153 for (String region: regions) { 154 if (CarPackageManager.DRIVING_SAFETY_REGION_ALL.equals(region)) { 155 return ALL_REGION_ONLY; 156 } 157 } 158 return Arrays.asList(regions); 159 } 160 throw new NameNotFoundException(); 161 } 162 isRegionSupported(String regionString, String currentRegion)163 private static boolean isRegionSupported(String regionString, String currentRegion) { 164 if (regionString.isEmpty()) { // not specified means all regions. 165 return true; 166 } 167 if (currentRegion.equals(CarPackageManager.DRIVING_SAFETY_REGION_ALL)) { 168 return true; 169 } 170 String[] regions = regionString.split(","); 171 for (String region: regions) { 172 if (CarPackageManager.DRIVING_SAFETY_REGION_ALL.equals(region)) { 173 return true; 174 } 175 if (currentRegion.equals(region)) { 176 return true; 177 } 178 } 179 // valid regions but does not match currentRegion. 180 if (CarPackageManagerService.DBG) { 181 Slog.d(TAG, 182 "isRegionSupported not supported, regionString:" + regionString 183 + " region:" + currentRegion); 184 } 185 return false; 186 } 187 } 188