• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.location.injector;
18 
19 import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
20 import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
21 
22 import static com.android.server.location.LocationManagerService.D;
23 import static com.android.server.location.LocationManagerService.TAG;
24 
25 import android.location.util.identity.CallerIdentity;
26 import android.util.ArrayMap;
27 import android.util.ArraySet;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.util.Map;
33 import java.util.Objects;
34 import java.util.Set;
35 
36 /**
37  * Helps manage appop monitoring for multiple location clients.
38  */
39 public class LocationAttributionHelper {
40 
41     private static class BucketKey {
42         private final String mBucket;
43         private final Object mKey;
44 
BucketKey(String bucket, Object key)45         private BucketKey(String bucket, Object key) {
46             mBucket = Objects.requireNonNull(bucket);
47             mKey = Objects.requireNonNull(key);
48         }
49 
50         @Override
equals(Object o)51         public boolean equals(Object o) {
52             if (this == o) {
53                 return true;
54             }
55             if (o == null || getClass() != o.getClass()) {
56                 return false;
57             }
58 
59             BucketKey that = (BucketKey) o;
60             return mBucket.equals(that.mBucket)
61                     && mKey.equals(that.mKey);
62         }
63 
64         @Override
hashCode()65         public int hashCode() {
66             return Objects.hash(mBucket, mKey);
67         }
68     }
69 
70     private final AppOpsHelper mAppOpsHelper;
71 
72     @GuardedBy("this")
73     private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
74     @GuardedBy("this")
75     private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
76 
LocationAttributionHelper(AppOpsHelper appOpsHelper)77     public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
78         mAppOpsHelper = appOpsHelper;
79 
80         mAttributions = new ArrayMap<>();
81         mHighPowerAttributions = new ArrayMap<>();
82     }
83 
84     /**
85      * Report normal location usage for the given caller in the given bucket, with a unique key.
86      */
reportLocationStart(CallerIdentity identity, String bucket, Object key)87     public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
88             Object key) {
89         Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
90                 i -> new ArraySet<>());
91         boolean empty = keySet.isEmpty();
92         if (keySet.add(new BucketKey(bucket, key)) && empty) {
93             if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
94                 mAttributions.remove(identity);
95             }
96         }
97     }
98 
99     /**
100      * Report normal location usage has stopped for the given caller in the given bucket, with a
101      * unique key.
102      */
reportLocationStop(CallerIdentity identity, String bucket, Object key)103     public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
104             Object key) {
105         Set<BucketKey> keySet = mAttributions.get(identity);
106         if (keySet != null && keySet.remove(new BucketKey(bucket, key))
107                 && keySet.isEmpty()) {
108             mAttributions.remove(identity);
109             mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
110         }
111     }
112 
113     /**
114      * Report high power location usage for the given caller in the given bucket, with a unique
115      * key.
116      */
reportHighPowerLocationStart(CallerIdentity identity, String bucket, Object key)117     public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
118             Object key) {
119         Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
120                 i -> new ArraySet<>());
121         boolean empty = keySet.isEmpty();
122         if (keySet.add(new BucketKey(bucket, key)) && empty) {
123             if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
124                 if (D) {
125                     Log.v(TAG, "starting high power location attribution for " + identity);
126                 }
127             } else {
128                 mHighPowerAttributions.remove(identity);
129             }
130         }
131     }
132 
133     /**
134      * Report high power location usage has stopped for the given caller in the given bucket,
135      * with a unique key.
136      */
reportHighPowerLocationStop(CallerIdentity identity, String bucket, Object key)137     public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
138             Object key) {
139         Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
140         if (keySet != null && keySet.remove(new BucketKey(bucket, key))
141                 && keySet.isEmpty()) {
142             if (D) {
143                 Log.v(TAG, "stopping high power location attribution for " + identity);
144             }
145             mHighPowerAttributions.remove(identity);
146             mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
147         }
148     }
149 }
150