Backport of backup transport whitelist

Sysconfig define a whitelist of permitted backup transports

Previously any apk bundled in priv-app could insert a backup transport.
Reduce risk surface by giving the OEM explicit control over who is
allowed to handle backup data.

Bug 28406080

Backport of 494df791728f4d42d67e935c327910975993ad29 from N

Change-Id: I9f90e324169a68720d608f74754d284a7e59cf87
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12003e2..30d90af 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -80,7 +80,9 @@
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -93,6 +95,7 @@
 import com.android.internal.backup.IObbBackupService;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 
@@ -300,6 +303,7 @@
     volatile boolean mClearingData;
 
     // Transport bookkeeping
+    final ArraySet<ComponentName> mTransportWhitelist;
     final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
     final ArrayMap<String,String> mTransportNames
             = new ArrayMap<String,String>();             // component name -> registration name
@@ -1084,11 +1088,15 @@
 
         // Set up our transport options and initialize the default transport
         // TODO: Don't create transports that we don't need to?
-        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+
+        String transport = Settings.Secure.getString(context.getContentResolver(),
                 Settings.Secure.BACKUP_TRANSPORT);
-        if ("".equals(mCurrentTransport)) {
-            mCurrentTransport = null;
+        if (TextUtils.isEmpty(transport)) {
+            transport = null;
         }
+        mCurrentTransport = transport;
         if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
 
         // Find all transport hosts and bind to their services
@@ -1099,11 +1107,11 @@
         }
         if (hosts != null) {
             for (int i = 0; i < hosts.size(); i++) {
-                final ServiceInfo transport = hosts.get(i).serviceInfo;
+                final ServiceInfo transportService = hosts.get(i).serviceInfo;
                 if (MORE_DEBUG) {
-                    Slog.v(TAG, "   " + transport.packageName + "/" + transport.name);
+                    Slog.v(TAG, "   " + transportService.packageName + "/" + transportService.name);
                 }
-                tryBindTransport(transport);
+                tryBindTransport(transportService);
             }
         }
 
@@ -1983,7 +1991,12 @@
     // Actually bind; presumes that we have already validated the transport service
     boolean bindTransport(ServiceInfo transport) {
         ComponentName svcName = new ComponentName(transport.packageName, transport.name);
-        if (MORE_DEBUG) {
+        if (!mTransportWhitelist.contains(svcName)) {
+            Slog.w(TAG, "Proposed transport " + svcName + " not whitelisted; ignoring");
+            return false;
+        }
+
+        if (DEBUG) {
             Slog.i(TAG, "Binding to transport host " + svcName);
         }
         Intent intent = new Intent(mTransportServiceIntent);
@@ -9636,6 +9649,12 @@
                     + " (now = " + System.currentTimeMillis() + ')');
             pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
 
+            pw.println("Transport whitelist:");
+            for (ComponentName transport : mTransportWhitelist) {
+                pw.print("    ");
+                pw.println(transport.flattenToShortString());
+            }
+
             pw.println("Available transports:");
             final String[] transports = listAllTransports();
             if (transports != null) {
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index cd61347..7ef3e12 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.os.*;
 import android.os.Process;
@@ -99,6 +100,9 @@
     // URL-handling state upon factory reset.
     final ArraySet<String> mLinkedApps = new ArraySet<>();
 
+    // These are the permitted backup transport service components
+    final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
+
     public static SystemConfig getInstance() {
         synchronized (SystemConfig.class) {
             if (sInstance == null) {
@@ -144,6 +148,10 @@
         return mLinkedApps;
     }
 
+    public ArraySet<ComponentName> getBackupTransportWhitelist() {
+        return mBackupTransportWhitelist;
+    }
+
     SystemConfig() {
         // Read configuration from system
         readPermissions(Environment.buildPath(
@@ -380,6 +388,23 @@
                         mLinkedApps.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("backup-transport-whitelisted-service".equals(name)) {
+                    String serviceName = parser.getAttributeValue(null, "service");
+                    if (serviceName == null) {
+                        Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
+                                + permFile + " at " + parser.getPositionDescription());
+                    } else {
+                        ComponentName cn = ComponentName.unflattenFromString(serviceName);
+                        if (cn == null) {
+                            Slog.w(TAG,
+                                    "<backup-transport-whitelisted-service> with invalid service name "
+                                    + serviceName + " in "+ permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            mBackupTransportWhitelist.add(cn);
+                        }
+                    }
+                    XmlUtils.skipCurrentTag(parser);
 
                 } else {
                     XmlUtils.skipCurrentTag(parser);