1 /*
2  * Copyright (C) 2024 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.compatibility.common.deviceinfo;
18 
19 import android.util.Log;
20 
21 import androidx.annotation.NonNull;
22 
23 import com.android.compatibility.common.util.DeviceInfoStore;
24 import com.android.compatibility.common.util.SystemUtil;
25 import com.android.modules.utils.build.SdkLevel;
26 
27 import java.io.IOException;
28 
29 /**
30  * PermissionDeviceInfo collector.
31  * <p>
32  * Information about permissions in the system, independent of per-package information which is
33  * collected in {@link PackageDeviceInfo}.
34  */
35 public class PermissionDeviceInfo extends DeviceInfo {
36 
37     private static final String LOG_TAG = "PermissionDeviceInfo";
38 
39     private static final String NAME = "name";
40     private static final String PACKAGES = "packages";
41     private static final String PARTITION = "partition";
42     private static final String PERMISSIONS = "permissions";
43     private static final String SIGNATURE_PERMISSION_ALLOWLIST_ENABLED =
44         "signature_permission_allowlist_enabled";
45     private static final String SIGNATURE_PERMISSION_ALLOWLISTS = "signature_permission_allowlists";
46 
47     @Override
collectDeviceInfo(@onNull DeviceInfoStore store)48     protected void collectDeviceInfo(@NonNull DeviceInfoStore store) throws Exception {
49         if (SdkLevel.isAtLeastV()) {
50             collectSignaturePermissionAllowlists(store);
51             collectSignaturePermissionAllowlistEnabled(store);
52         }
53     }
54 
collectSignaturePermissionAllowlists(@onNull DeviceInfoStore store)55     private static void collectSignaturePermissionAllowlists(@NonNull DeviceInfoStore store)
56             throws IOException {
57         store.startArray(SIGNATURE_PERMISSION_ALLOWLISTS);
58         collectSignaturePermissionAllowlist(store, "system");
59         collectSignaturePermissionAllowlist(store, "vendor");
60         collectSignaturePermissionAllowlist(store, "product");
61         collectSignaturePermissionAllowlist(store, "system-ext");
62         collectSignaturePermissionAllowlist(store, "apex");
63         store.endArray(); // "signature_permission_allowlists"
64     }
65 
collectSignaturePermissionAllowlist(@onNull DeviceInfoStore store, @NonNull String partition)66     private static void collectSignaturePermissionAllowlist(@NonNull DeviceInfoStore store,
67             @NonNull String partition) throws IOException {
68         final String output = SystemUtil.runShellCommandOrThrow(
69                 "pm get-signature-permission-allowlist " + partition).trim();
70         store.startGroup();
71         store.addResult(PARTITION, partition);
72         store.startArray(PACKAGES);
73         boolean isFirstPackage = true;
74         for (String line : output.split("\n")) {
75             line = line.trim();
76             if (line.startsWith("Package: ")) {
77                 if (isFirstPackage) {
78                     isFirstPackage = false;
79                 } else {
80                     store.endArray(); // "permissions"
81                     store.endGroup();
82                 }
83                 String packageName = line.substring("Package: ".length());
84                 store.startGroup();
85                 store.addResult(NAME, packageName);
86                 store.startArray(PERMISSIONS);
87             } else if (line.startsWith("Permission: ")) {
88                 String permissionName = line.substring("Permission: ".length());
89                 store.startGroup();
90                 store.addResult(NAME, permissionName);
91                 store.endGroup();
92             } else if (line.isEmpty()) {
93                 // Ignored.
94             } else {
95                 throw new IllegalArgumentException("Unknown line: " + line);
96             }
97         }
98         if (!isFirstPackage) {
99             store.endArray(); // "permissions"
100             store.endGroup();
101         }
102         store.endArray(); // "packages"
103         store.endGroup();
104     }
105 
collectSignaturePermissionAllowlistEnabled(@onNull DeviceInfoStore store)106     private static void collectSignaturePermissionAllowlistEnabled(@NonNull DeviceInfoStore store)
107             throws IOException {
108         Boolean enabled = FeatureFlagUtils.isFeatureFlagEnabled(
109                 "permissions/android.permission.flags.signature_permission_allowlist_enabled",
110                 "system");
111         if (enabled == null) {
112             Log.e(LOG_TAG, "Failed to check whether signature permission allowlist is enabled");
113             return;
114         }
115         store.addResult(SIGNATURE_PERMISSION_ALLOWLIST_ENABLED, enabled);
116     }
117 }
118