1 /*
2  * Copyright (C) 2018 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.packageinstaller.permission.model;
18 
19 import android.app.AppOpsManager;
20 import android.app.AppOpsManager.HistoricalOp;
21 import android.app.AppOpsManager.HistoricalPackageOps;
22 import android.app.AppOpsManager.OpEntry;
23 import android.app.AppOpsManager.PackageOps;
24 
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 
28 import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.function.Function;
33 
34 /**
35  * Stats for permission usage of an app. This data is for a given time period,
36  * i.e. does not contain the full history.
37  */
38 public final class AppPermissionUsage {
39     private final @NonNull List<GroupUsage> mGroupUsages = new ArrayList<>();
40     private final @NonNull PermissionApp mPermissionApp;
41 
AppPermissionUsage(@onNull PermissionApp permissionApp, @NonNull List<AppPermissionGroup> groups, @Nullable PackageOps lastUsage, @Nullable HistoricalPackageOps historicalUsage)42     private AppPermissionUsage(@NonNull PermissionApp permissionApp,
43             @NonNull List<AppPermissionGroup> groups, @Nullable PackageOps lastUsage,
44             @Nullable HistoricalPackageOps historicalUsage) {
45         mPermissionApp = permissionApp;
46         final int groupCount = groups.size();
47         for (int i = 0; i < groupCount; i++) {
48             final AppPermissionGroup group = groups.get(i);
49             mGroupUsages.add(new GroupUsage(group, lastUsage, historicalUsage));
50         }
51     }
52 
getApp()53     public @NonNull PermissionApp getApp() {
54         return mPermissionApp;
55     }
56 
getPackageName()57     public @NonNull String getPackageName() {
58         return mPermissionApp.getPackageName();
59     }
60 
getUid()61     public int getUid() {
62         return mPermissionApp.getUid();
63     }
64 
getLastAccessTime()65     public long getLastAccessTime() {
66         long lastAccessTime = 0;
67         final int permissionCount = mGroupUsages.size();
68         for (int i = 0; i < permissionCount; i++) {
69             final GroupUsage groupUsage = mGroupUsages.get(i);
70             lastAccessTime = Math.max(lastAccessTime, groupUsage.getLastAccessTime());
71         }
72         return lastAccessTime;
73     }
74 
getAccessCount()75     public long getAccessCount() {
76         long accessCount = 0;
77         final int permissionCount = mGroupUsages.size();
78         for (int i = 0; i < permissionCount; i++) {
79             final GroupUsage permission = mGroupUsages.get(i);
80             accessCount += permission.getAccessCount();
81         }
82         return accessCount;
83     }
84 
getGroupUsages()85     public @NonNull List<GroupUsage> getGroupUsages() {
86         return mGroupUsages;
87     }
88 
89     /**
90      * Stats for permission usage of a permission group. This data is for a
91      * given time period, i.e. does not contain the full history.
92      */
93     public static class GroupUsage {
94         private final @NonNull AppPermissionGroup mGroup;
95         private final @Nullable PackageOps mLastUsage;
96         private final @Nullable HistoricalPackageOps mHistoricalUsage;
97 
GroupUsage(@onNull AppPermissionGroup group, @Nullable PackageOps lastUsage, @Nullable HistoricalPackageOps historicalUsage)98         GroupUsage(@NonNull AppPermissionGroup group, @Nullable PackageOps lastUsage,
99                 @Nullable HistoricalPackageOps historicalUsage) {
100             mGroup = group;
101             mLastUsage = lastUsage;
102             mHistoricalUsage = historicalUsage;
103         }
104 
getLastAccessTime()105         public long getLastAccessTime() {
106             if (mLastUsage == null) {
107                 return 0;
108             }
109             return lastAccessAggregate(
110                     (op) -> op.getLastAccessTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
111         }
112 
getLastAccessForegroundTime()113         public long getLastAccessForegroundTime() {
114             if (mLastUsage == null) {
115                 return 0;
116             }
117             return lastAccessAggregate(
118                     (op) -> op.getLastAccessForegroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
119         }
120 
getLastAccessBackgroundTime()121         public long getLastAccessBackgroundTime() {
122             if (mLastUsage == null) {
123                 return 0;
124             }
125             return lastAccessAggregate(
126                     (op) -> op.getLastAccessBackgroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
127         }
128 
getForegroundAccessCount()129         public long getForegroundAccessCount() {
130             if (mHistoricalUsage == null) {
131                 return 0;
132             }
133             return extractAggregate((HistoricalOp op)
134                     -> op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
135         }
136 
getBackgroundAccessCount()137         public long getBackgroundAccessCount() {
138             if (mHistoricalUsage == null) {
139                 return 0;
140             }
141             return extractAggregate((HistoricalOp op)
142                     -> op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
143         }
144 
getAccessCount()145         public long getAccessCount() {
146             if (mHistoricalUsage == null) {
147                 return 0;
148             }
149             return extractAggregate((HistoricalOp op) ->
150                 op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
151                         + op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
152             );
153         }
154 
getAccessDuration()155         public long getAccessDuration() {
156             if (mHistoricalUsage == null) {
157                 return 0;
158             }
159             return extractAggregate((HistoricalOp op) ->
160                     op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
161                             + op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
162             );
163         }
164 
isRunning()165         public boolean isRunning() {
166             if (mLastUsage == null) {
167                 return false;
168             }
169             final ArrayList<Permission> permissions = mGroup.getPermissions();
170             final int permissionCount = permissions.size();
171             for (int i = 0; i < permissionCount; i++) {
172                 final Permission permission = permissions.get(i);
173                 final String opName = permission.getAppOp();
174                 final List<OpEntry> ops = mLastUsage.getOps();
175                 final int opCount = ops.size();
176                 for (int j = 0; j < opCount; j++) {
177                     final OpEntry op = ops.get(j);
178                     if (op.getOpStr().equals(opName) && op.isRunning()) {
179                         return true;
180                     }
181                 }
182             }
183             return false;
184         }
185 
extractAggregate(@onNull Function<HistoricalOp, Long> extractor)186         private long extractAggregate(@NonNull Function<HistoricalOp, Long> extractor) {
187             long aggregate = 0;
188             final ArrayList<Permission> permissions = mGroup.getPermissions();
189             final int permissionCount = permissions.size();
190             for (int i = 0; i < permissionCount; i++) {
191                 final Permission permission = permissions.get(i);
192                 final String opName = permission.getAppOp();
193                 final HistoricalOp historicalOp = mHistoricalUsage.getOp(opName);
194                 if (historicalOp != null) {
195                     aggregate += extractor.apply(historicalOp);
196                 }
197             }
198             return aggregate;
199         }
200 
lastAccessAggregate(@onNull Function<OpEntry, Long> extractor)201         private long lastAccessAggregate(@NonNull Function<OpEntry, Long> extractor) {
202             long aggregate = 0;
203             final ArrayList<Permission> permissions = mGroup.getPermissions();
204             final int permissionCount = permissions.size();
205             for (int permissionNum = 0; permissionNum < permissionCount; permissionNum++) {
206                 final Permission permission = permissions.get(permissionNum);
207                 final String opName = permission.getAppOp();
208                 final List<OpEntry> ops = mLastUsage.getOps();
209                 final int opCount = ops.size();
210                 for (int opNum = 0; opNum < opCount; opNum++) {
211                     final OpEntry op = ops.get(opNum);
212                     if (op.getOpStr().equals(opName)) {
213                         aggregate = Math.max(aggregate, extractor.apply(op));
214                     }
215                 }
216             }
217             return aggregate;
218         }
219 
getGroup()220         public @NonNull AppPermissionGroup getGroup() {
221             return mGroup;
222         }
223     }
224 
225     public static class Builder {
226         private final @NonNull List<AppPermissionGroup> mGroups = new ArrayList<>();
227         private final @NonNull PermissionApp mPermissionApp;
228         private @Nullable PackageOps mLastUsage;
229         private @Nullable HistoricalPackageOps mHistoricalUsage;
230 
Builder(@onNull PermissionApp permissionApp)231         public Builder(@NonNull PermissionApp permissionApp) {
232             mPermissionApp = permissionApp;
233         }
234 
addGroup(@onNull AppPermissionGroup group)235         public @NonNull Builder addGroup(@NonNull AppPermissionGroup group) {
236             mGroups.add(group);
237             return this;
238         }
239 
setLastUsage(@ullable PackageOps lastUsage)240         public @NonNull Builder setLastUsage(@Nullable PackageOps lastUsage) {
241             mLastUsage = lastUsage;
242             return this;
243         }
244 
setHistoricalUsage(@ullable HistoricalPackageOps historicalUsage)245         public @NonNull Builder setHistoricalUsage(@Nullable HistoricalPackageOps historicalUsage) {
246             mHistoricalUsage = historicalUsage;
247             return this;
248         }
249 
build()250         public @NonNull AppPermissionUsage build() {
251             if (mGroups.isEmpty()) {
252                 throw new IllegalStateException("mGroups cannot be empty.");
253             }
254             return new AppPermissionUsage(mPermissionApp, mGroups, mLastUsage, mHistoricalUsage);
255         }
256     }
257 }
258