1 /* 2 * Copyright (C) 2021 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.wallpaper; 18 19 import android.app.ILocalWallpaperColorConsumer; 20 import android.graphics.RectF; 21 import android.os.IBinder; 22 import android.os.RemoteCallbackList; 23 import android.os.RemoteException; 24 import android.util.ArrayMap; 25 import android.util.ArraySet; 26 import android.util.SparseArray; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.function.Consumer; 33 34 /** 35 * Manages the lifecycle of local wallpaper color callbacks and their interested wallpaper regions. 36 */ 37 public class LocalColorRepository { 38 /** 39 * Maps local wallpaper color callbacks' binders to their interested wallpaper regions, which 40 * are stored in a map of display Ids to wallpaper regions. 41 * binder callback -> [display id: int] -> areas 42 */ 43 ArrayMap<IBinder, SparseArray<ArraySet<RectF>>> mLocalColorAreas = new ArrayMap(); 44 RemoteCallbackList<ILocalWallpaperColorConsumer> mCallbacks = new RemoteCallbackList(); 45 46 /** 47 * Add areas to a consumer 48 * @param consumer 49 * @param areas 50 * @param displayId 51 */ addAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas, int displayId)52 public void addAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas, int displayId) { 53 IBinder binder = consumer.asBinder(); 54 SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder); 55 ArraySet<RectF> displayAreas = null; 56 if (displays == null) { 57 try { 58 consumer.asBinder().linkToDeath(() -> 59 mLocalColorAreas.remove(consumer.asBinder()), 0); 60 } catch (RemoteException e) { 61 e.printStackTrace(); 62 } 63 displays = new SparseArray<>(); 64 mLocalColorAreas.put(binder, displays); 65 } else { 66 displayAreas = displays.get(displayId); 67 } 68 if (displayAreas == null) { 69 displayAreas = new ArraySet(areas); 70 displays.put(displayId, displayAreas); 71 } 72 73 for (int i = 0; i < areas.size(); i++) { 74 displayAreas.add(areas.get(i)); 75 } 76 mCallbacks.register(consumer); 77 } 78 79 /** 80 * remove an area for a consumer 81 * @param consumer 82 * @param areas 83 * @param displayId 84 * @return the areas that are removed from all callbacks 85 */ removeAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas, int displayId)86 public List<RectF> removeAreas(ILocalWallpaperColorConsumer consumer, List<RectF> areas, 87 int displayId) { 88 IBinder binder = consumer.asBinder(); 89 SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder); 90 ArraySet<RectF> registeredAreas = null; 91 if (displays != null) { 92 registeredAreas = displays.get(displayId); 93 if (registeredAreas == null) { 94 mCallbacks.unregister(consumer); 95 } else { 96 for (int i = 0; i < areas.size(); i++) { 97 registeredAreas.remove(areas.get(i)); 98 } 99 if (registeredAreas.size() == 0) { 100 displays.remove(displayId); 101 } 102 } 103 if (displays.size() == 0) { 104 mLocalColorAreas.remove(binder); 105 mCallbacks.unregister(consumer); 106 } 107 } else { 108 mCallbacks.unregister(consumer); 109 } 110 ArraySet<RectF> purged = new ArraySet<>(areas); 111 for (int i = 0; i < mLocalColorAreas.size(); i++) { 112 for (int j = 0; j < mLocalColorAreas.valueAt(i).size(); j++) { 113 for (int k = 0; k < mLocalColorAreas.valueAt(i).valueAt(j).size(); k++) { 114 purged.remove(mLocalColorAreas.valueAt(i).valueAt(j).valueAt(k)); 115 } 116 } 117 } 118 return new ArrayList(purged); 119 } 120 121 /** 122 * Return the local areas by display id 123 * @param displayId 124 * @return 125 */ getAreasByDisplayId(int displayId)126 public List<RectF> getAreasByDisplayId(int displayId) { 127 ArrayList<RectF> areas = new ArrayList(); 128 for (int i = 0; i < mLocalColorAreas.size(); i++) { 129 SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.valueAt(i); 130 if (displays == null) continue; 131 ArraySet<RectF> displayAreas = displays.get(displayId); 132 if (displayAreas == null) continue; 133 for (int j = 0; j < displayAreas.size(); j++) { 134 areas.add(displayAreas.valueAt(j)); 135 } 136 } 137 return areas; 138 } 139 140 /** 141 * invoke a callback for each area of interest 142 * @param callback 143 * @param area 144 * @param displayId 145 */ forEachCallback(Consumer<ILocalWallpaperColorConsumer> callback, RectF area, int displayId)146 public void forEachCallback(Consumer<ILocalWallpaperColorConsumer> callback, 147 RectF area, int displayId) { 148 mCallbacks.broadcast(cb -> { 149 IBinder binder = cb.asBinder(); 150 SparseArray<ArraySet<RectF>> displays = mLocalColorAreas.get(binder); 151 if (displays == null) return; 152 ArraySet<RectF> displayAreas = displays.get(displayId); 153 if (displayAreas != null && displayAreas.contains(area)) callback.accept(cb); 154 }); 155 } 156 157 /** 158 * For testing 159 * @param callback 160 * @return if the callback is registered 161 */ 162 @VisibleForTesting isCallbackAvailable(ILocalWallpaperColorConsumer callback)163 protected boolean isCallbackAvailable(ILocalWallpaperColorConsumer callback) { 164 return mLocalColorAreas.get(callback.asBinder()) != null; 165 } 166 } 167