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.display; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.pm.PackageManagerInternal; 23 import android.os.UserHandle; 24 import android.provider.DeviceConfig; 25 import android.provider.DeviceConfigInterface; 26 import android.util.ArrayMap; 27 import android.util.SparseArray; 28 29 import com.android.internal.R; 30 import com.android.internal.annotations.GuardedBy; 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.os.BackgroundThread; 33 import com.android.server.LocalServices; 34 import com.android.server.pm.pkg.PackageStateInternal; 35 36 import java.io.PrintWriter; 37 import java.util.Map; 38 39 final class SmallAreaDetectionController { nativeUpdateSmallAreaDetection(int[] appIds, float[] thresholds)40 private static native void nativeUpdateSmallAreaDetection(int[] appIds, float[] thresholds); nativeSetSmallAreaDetectionThreshold(int appId, float threshold)41 private static native void nativeSetSmallAreaDetectionThreshold(int appId, float threshold); 42 43 // TODO(b/281720315): Move this to DeviceConfig once server side ready. 44 private static final String KEY_SMALL_AREA_DETECTION_ALLOWLIST = 45 "small_area_detection_allowlist"; 46 47 private final Object mLock = new Object(); 48 private final Context mContext; 49 private final PackageManagerInternal mPackageManager; 50 @GuardedBy("mLock") 51 private final Map<String, Float> mAllowPkgMap = new ArrayMap<>(); 52 create(@onNull Context context)53 static SmallAreaDetectionController create(@NonNull Context context) { 54 final SmallAreaDetectionController controller = 55 new SmallAreaDetectionController(context, DeviceConfigInterface.REAL); 56 final String property = DeviceConfigInterface.REAL.getProperty( 57 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_SMALL_AREA_DETECTION_ALLOWLIST); 58 controller.updateAllowlist(property); 59 return controller; 60 } 61 62 @VisibleForTesting SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig)63 SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig) { 64 mContext = context; 65 mPackageManager = LocalServices.getService(PackageManagerInternal.class); 66 deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 67 BackgroundThread.getExecutor(), 68 new SmallAreaDetectionController.OnPropertiesChangedListener()); 69 mPackageManager.getPackageList(new PackageReceiver()); 70 } 71 72 @VisibleForTesting updateAllowlist(@ullable String property)73 void updateAllowlist(@Nullable String property) { 74 final Map<String, Float> allowPkgMap = new ArrayMap<>(); 75 synchronized (mLock) { 76 mAllowPkgMap.clear(); 77 if (property != null) { 78 final String[] mapStrings = property.split(","); 79 for (String mapString : mapStrings) putToAllowlist(mapString); 80 } else { 81 final String[] defaultMapStrings = mContext.getResources() 82 .getStringArray(R.array.config_smallAreaDetectionAllowlist); 83 for (String defaultMapString : defaultMapStrings) putToAllowlist(defaultMapString); 84 } 85 86 if (mAllowPkgMap.isEmpty()) return; 87 allowPkgMap.putAll(mAllowPkgMap); 88 } 89 updateSmallAreaDetection(allowPkgMap); 90 } 91 92 @GuardedBy("mLock") putToAllowlist(String rowData)93 private void putToAllowlist(String rowData) { 94 // Data format: package:threshold - e.g. "com.abc.music:0.05" 95 final String[] items = rowData.split(":"); 96 if (items.length == 2) { 97 try { 98 final String pkg = items[0]; 99 final float threshold = Float.valueOf(items[1]); 100 mAllowPkgMap.put(pkg, threshold); 101 } catch (Exception e) { 102 // Just skip if items[1] - the threshold is not parsable number 103 } 104 } 105 } 106 updateSmallAreaDetection(Map<String, Float> allowPkgMap)107 private void updateSmallAreaDetection(Map<String, Float> allowPkgMap) { 108 final SparseArray<Float> appIdThresholdList = new SparseArray(allowPkgMap.size()); 109 for (String pkg : allowPkgMap.keySet()) { 110 final float threshold = allowPkgMap.get(pkg); 111 final PackageStateInternal stage = mPackageManager.getPackageStateInternal(pkg); 112 if (stage != null) { 113 appIdThresholdList.put(stage.getAppId(), threshold); 114 } 115 } 116 117 final int[] appIds = new int[appIdThresholdList.size()]; 118 final float[] thresholds = new float[appIdThresholdList.size()]; 119 for (int i = 0; i < appIdThresholdList.size(); i++) { 120 appIds[i] = appIdThresholdList.keyAt(i); 121 thresholds[i] = appIdThresholdList.valueAt(i); 122 } 123 updateSmallAreaDetection(appIds, thresholds); 124 } 125 126 @VisibleForTesting updateSmallAreaDetection(int[] appIds, float[] thresholds)127 void updateSmallAreaDetection(int[] appIds, float[] thresholds) { 128 nativeUpdateSmallAreaDetection(appIds, thresholds); 129 } 130 setSmallAreaDetectionThreshold(int appId, float threshold)131 void setSmallAreaDetectionThreshold(int appId, float threshold) { 132 nativeSetSmallAreaDetectionThreshold(appId, threshold); 133 } 134 dump(PrintWriter pw)135 void dump(PrintWriter pw) { 136 pw.println("Small area detection allowlist"); 137 pw.println(" Packages:"); 138 synchronized (mLock) { 139 for (String pkg : mAllowPkgMap.keySet()) { 140 pw.println(" " + pkg + " threshold = " + mAllowPkgMap.get(pkg)); 141 } 142 } 143 } 144 145 private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener { onPropertiesChanged(@onNull DeviceConfig.Properties properties)146 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { 147 if (properties.getKeyset().contains(KEY_SMALL_AREA_DETECTION_ALLOWLIST)) { 148 updateAllowlist( 149 properties.getString(KEY_SMALL_AREA_DETECTION_ALLOWLIST, null /*default*/)); 150 } 151 } 152 } 153 154 private final class PackageReceiver implements PackageManagerInternal.PackageListObserver { 155 @Override onPackageAdded(@onNull String packageName, int uid)156 public void onPackageAdded(@NonNull String packageName, int uid) { 157 float threshold = 0.0f; 158 synchronized (mLock) { 159 if (mAllowPkgMap.containsKey(packageName)) { 160 threshold = mAllowPkgMap.get(packageName); 161 } 162 } 163 if (threshold > 0.0f) { 164 setSmallAreaDetectionThreshold(UserHandle.getAppId(uid), threshold); 165 } 166 } 167 } 168 } 169