Add a new atom to log when an app is moved from internal to external
storage and vice versa.

Bug: 123688171
Test: Manually tested using stats_testdrive script.
Change-Id: Ie6c31388b779421796554465f87a4dbdf742d7e1
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index c2b81e4..a752a9f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -254,6 +254,7 @@
         PrivacyIndicatorsInteracted privacy_indicators_interacted = 180;
         AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
         NetworkStackReported network_stack_reported = 182;
+        AppMovedStorageReported app_moved_storage_reported = 183;
     }
 
     // Pulled events will start at field 10000.
@@ -3445,6 +3446,30 @@
     optional string package_name = 2;
 }
 
+/**
+ * Logs information about a package that is moved from the internal to external storage and vice
+ * versa.
+ * It logs the package name, the type of the external storage where the package is installed
+ * (if moved to external storage, or UNKNOWN if moved to internal storage),
+ * and the move type: if it's from internal to external or the other way around.
+ *
+ * Logged from:
+        frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
+ */
+message AppMovedStorageReported {
+    enum MoveType {
+        UNKNOWN = 0;
+        TO_EXTERNAL = 1;
+        TO_INTERNAL = 2;
+    }
+    // The type of the external storage.
+    optional android.stats.storage.ExternalStorageType external_storage_type = 1;
+    // The type of move.
+    optional MoveType move_type = 2;
+    // The name of the package that was moved.
+    optional string package_name = 3;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 81448b7..5726d72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1938,8 +1938,13 @@
                 // Send broadcast package appeared if external for all users
                 if (isExternal(res.pkg)) {
                     if (!update) {
+                        final StorageManager storage =
+                                mContext.getSystemService(StorageManager.class);
+                        VolumeInfo volume =
+                                storage.findVolumeByUuid(
+                                        res.pkg.applicationInfo.storageUuid.toString());
                         int packageExternalStorageType =
-                                getPackageExternalStorageType(res.pkg);
+                                getPackageExternalStorageType(volume, isExternal(res.pkg));
                         // If the package was installed externally, log it.
                         if (packageExternalStorageType != StorageEnums.UNKNOWN) {
                             StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
@@ -2036,15 +2041,16 @@
 
     /**
      * Gets the type of the external storage a package is installed on.
-     * @param pkg The package for which to get the external storage type.
-     * @return {@link StorageEnum#TYPE_UNKNOWN} if it is not stored externally or the corresponding
-     * {@link StorageEnum} storage type value if it is.
+     * @param packageVolume The storage volume of the package.
+     * @param packageIsExternal true if the package is currently installed on
+     * external/removable/unprotected storage.
+     * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
+     * corresponding {@link StorageEnum} storage type value if it is.
      */
-    private int getPackageExternalStorageType(PackageParser.Package pkg) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString());
-        if (volume != null) {
-            DiskInfo disk = volume.getDisk();
+    private static int getPackageExternalStorageType(VolumeInfo packageVolume,
+            boolean packageIsExternal) {
+        if (packageVolume != null) {
+            DiskInfo disk = packageVolume.getDisk();
             if (disk != null) {
                 if (disk.isSd()) {
                     return StorageEnums.SD_CARD;
@@ -2052,7 +2058,7 @@
                 if (disk.isUsb()) {
                     return StorageEnums.USB;
                 }
-                if (isExternal(pkg)) {
+                if (packageIsExternal) {
                     return StorageEnums.OTHER;
                 }
             }
@@ -22483,6 +22489,7 @@
         final int targetSdkVersion;
         final PackageFreezer freezer;
         final int[] installedUserIds;
+        final boolean isCurrentLocationExternal;
 
         // reader
         synchronized (mPackages) {
@@ -22529,6 +22536,7 @@
                         "Failed to move already frozen package");
             }
 
+            isCurrentLocationExternal = isExternal(pkg);
             codeFile = new File(pkg.codePath);
             installerPackageName = ps.installerPackageName;
             packageAbiOverride = ps.cpuAbiOverrideString;
@@ -22631,6 +22639,7 @@
                     case PackageInstaller.STATUS_SUCCESS:
                         mMoveCallbacks.notifyStatusChanged(moveId,
                                 PackageManager.MOVE_SUCCEEDED);
+                        logAppMovedStorage(packageName, isCurrentLocationExternal);
                         break;
                     case PackageInstaller.STATUS_FAILURE_STORAGE:
                         mMoveCallbacks.notifyStatusChanged(moveId,
@@ -22689,6 +22698,36 @@
         mHandler.sendMessage(msg);
     }
 
+    /**
+     * Logs that an app has been moved from internal to external storage and vice versa.
+     * @param packageName The package that was moved.
+     */
+    private void logAppMovedStorage(String packageName, boolean isPreviousLocationExternal) {
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+        if (pkg == null) {
+            return;
+        }
+
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString());
+        int packageExternalStorageType = getPackageExternalStorageType(volume, isExternal(pkg));
+
+        if (!isPreviousLocationExternal && isExternal(pkg)) {
+            // Move from internal to external storage.
+            StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
+                    StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
+                    packageName);
+        } else if (isPreviousLocationExternal && !isExternal(pkg)) {
+            // Move from external to internal storage.
+            StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
+                    StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
+                    packageName);
+        }
+    }
+
     @Override
     public int movePrimaryStorage(String volumeUuid) throws RemoteException {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);