Pull all current role holders into statsd

Create a new atom RoleHolder that maps a uid -> role and add the
mappings that currently exist in RoleManagerService

Test: - ./out/host/linux-x86/bin/statsd_testdrive 10049
      - adb shell cmd stats pull-source 10049
Bug: 123594188
Change-Id: Ib0fa60b07a95c06a219f3e3b37d51f59b624a017
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e84eced..93f700c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -269,6 +269,7 @@
         DebugElapsedClock debug_elapsed_clock = 10046;
         DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
         NumBiometricsEnrolled num_faces_enrolled = 10048;
+        RoleHolder role_holder = 10049;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3420,6 +3421,20 @@
     optional int32 num_enrolled = 2;
 }
 
+/**
+ * A mapping of role holder -> role
+ */
+message RoleHolder {
+    // uid of the role holder
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // package name of the role holder
+    optional string package_name = 2;
+
+    // the role held
+    optional string role = 3;
+}
+
 message AggStats {
     optional int64 min = 1;
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ba7bcc4..9b0fbef 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -220,6 +220,9 @@
         // BuildInformation.
         {android::util::BUILD_INFORMATION,
          {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+        // RoleHolder.
+        {android::util::ROLE_HOLDER,
+         {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 7d8c7c9..78ea397 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -52,6 +52,7 @@
 import android.service.sms.FinancialSmsService;
 import android.telephony.IFinancialSmsCallback;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.PackageUtils;
 import android.util.Slog;
@@ -145,6 +146,9 @@
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
+        LocalServices.addService(RoleManagerServiceInternal.class,
+                new RoleManagerServiceInternalImpl());
+
         registerUserRemovedReceiver();
     }
 
@@ -381,6 +385,19 @@
         }
     }
 
+    /**
+     * Get all roles and packages hold them.
+     *
+     * @param user The user to query to roles for
+     *
+     * @return The roles and their holders
+     */
+    @NonNull
+    private ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) {
+        RoleUserState userState = getOrCreateUserState(user.getIdentifier());
+        return userState.getRoleHolders();
+    }
+
     private class Stub extends IRoleManager.Stub {
 
         @Override
@@ -675,4 +692,16 @@
             }
         }
     }
+
+    /**
+     * Entry point for internal calls into role manager
+     */
+    private final class RoleManagerServiceInternalImpl extends RoleManagerServiceInternal {
+
+        @NonNull
+        @Override
+        public ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) {
+            return RoleManagerService.this.getRoleHoldersAsUser(user);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerServiceInternal.java b/services/core/java/com/android/server/role/RoleManagerServiceInternal.java
new file mode 100644
index 0000000..3afc3f7
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerServiceInternal.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.role;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+/**
+ * Internal calls into {@link RoleManagerService}
+ */
+public abstract class RoleManagerServiceInternal {
+    /**
+     * Get all roles and packages hold them.
+     *
+     * @param user The user to query to roles for
+     *
+     * @return The roles and their holders
+     */
+    @NonNull
+    public abstract ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(
+            @NonNull UserHandle user);
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 02dcc49..bc68dde 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -376,7 +376,7 @@
 
             version = mVersion;
             packagesHash = mPackagesHash;
-            roles = snapshotRolesLocked();
+            roles = getRoleHolders();
         }
 
         AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
@@ -541,7 +541,7 @@
 
             version = mVersion;
             packagesHash = mPackagesHash;
-            roles = snapshotRolesLocked();
+            roles = getRoleHolders();
         }
 
         long fieldToken = dumpOutputStream.start(fieldName, fieldId);
@@ -570,17 +570,24 @@
         dumpOutputStream.end(fieldToken);
     }
 
-    @GuardedBy("mLock")
-    private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
-        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
-        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
-            String roleName = mRoles.keyAt(i);
-            ArraySet<String> roleHolders = mRoles.valueAt(i);
+    /**
+     * Get the roles and their holders.
+     *
+     * @return A copy of the roles and their holders
+     */
+    @NonNull
+    public ArrayMap<String, ArraySet<String>> getRoleHolders() {
+        synchronized (mLock) {
+            ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
+            for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
+                String roleName = mRoles.keyAt(i);
+                ArraySet<String> roleHolders = mRoles.valueAt(i);
 
-            roleHolders = new ArraySet<>(roleHolders);
-            roles.put(roleName, roleHolders);
+                roleHolders = new ArraySet<>(roleHolders);
+                roles.put(roleName, roleHolders);
+            }
+            return roles;
         }
-        return roles;
     }
 
     /**
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c6d2870..2be78fe3 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -23,6 +23,7 @@
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
 import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -83,6 +84,7 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -112,6 +114,7 @@
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.role.RoleManagerServiceInternal;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
@@ -1781,6 +1784,60 @@
     }
 
     /**
+     * Add a RoleHolder atom for each package that holds a role.
+     *
+     * @param elapsedNanos the time since boot
+     * @param wallClockNanos the time on the clock
+     * @param pulledData the data sink to write to
+     */
+    private void pullRoleHolders(long elapsedNanos, final long wallClockNanos,
+            @NonNull List<StatsLogEventWrapper> pulledData) {
+        long callingToken = Binder.clearCallingIdentity();
+        try {
+            PackageManager pm = mContext.getPackageManager();
+            RoleManagerServiceInternal rm =
+                    LocalServices.getService(RoleManagerServiceInternal.class);
+
+            List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+
+            int numUsers = users.size();
+            for (int userNum = 0; userNum < numUsers; userNum++) {
+                UserHandle user = users.get(userNum).getUserHandle();
+
+                ArrayMap<String, ArraySet<String>> roles = rm.getRoleHoldersAsUser(user);
+
+                int numRoles = roles.size();
+                for (int roleNum = 0; roleNum < numRoles; roleNum++) {
+                    String roleName = roles.keyAt(roleNum);
+                    ArraySet<String> holders = roles.valueAt(roleNum);
+
+                    int numHolders = holders.size();
+                    for (int holderNum = 0; holderNum < numHolders; holderNum++) {
+                        String holderName = holders.valueAt(holderNum);
+
+                        PackageInfo pkg;
+                        try {
+                            pkg = pm.getPackageInfoAsUser(holderName, 0, user.getIdentifier());
+                        } catch (PackageManager.NameNotFoundException e) {
+                            Log.w(TAG, "Role holder " + holderName + " not found");
+                            return;
+                        }
+
+                        StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.ROLE_HOLDER,
+                                elapsedNanos, wallClockNanos);
+                        e.writeInt(pkg.applicationInfo.uid);
+                        e.writeString(holderName);
+                        e.writeString(roleName);
+                        pulledData.add(e);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+    }
+
+    /**
      * Pulls various data.
      */
     @Override // Binder call
@@ -1954,6 +2011,10 @@
                 pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.ROLE_HOLDER: {
+                pullRoleHolders(elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;