privapp_permissions: Check product privapp_permissions

* permission whitelists need to get placed on the same partition as the apps themselves
* check product and system individually and generate XMLs accordingly

Test: Verify missing entries for system and product get generated on hotdog
Test: Verify missing entries for system and system/product get generated on taimen and maple
Change-Id: I37bd5c7b738caaab93019688c6a944dc6bbffa75
diff --git a/tools/privapp_permissions/privapp_permissions.py b/tools/privapp_permissions/privapp_permissions.py
index 4016573..d61319e 100755
--- a/tools/privapp_permissions/privapp_permissions.py
+++ b/tools/privapp_permissions/privapp_permissions.py
@@ -38,7 +38,7 @@
 Usage:
     Specify which apk to generate priv-app permissions for. If no apk is \
 specified, this will default to all APKs under "<ANDROID_PRODUCT_OUT>/ \
-system/priv-app".
+system/priv-app and (system/)product/priv-app".
 
 Examples:
 
@@ -156,9 +156,18 @@
                 'You must either set up your build environment, or specify a '
                 'device to run against. See --help for more info.')
 
-        self.privapp_apks = self._resolve_apks(apks)
-        self.permissions_dir = self._resolve_sys_path('system/etc/permissions')
-        self.sysconfig_dir = self._resolve_sys_path('system/etc/sysconfig')
+        self.system_privapp_apks, self.product_privapp_apks =(
+                self._resolve_apks(apks))
+        self.system_permissions_dir = (
+                self._resolve_sys_path('system/etc/permissions'))
+        self.system_sysconfig_dir = (
+                self._resolve_sys_path('system/etc/sysconfig'))
+        self.product_permissions_dir = (
+                self._resolve_sys_path('product/etc/permissions',
+                                       'system/product/etc/permissions'))
+        self.product_sysconfig_dir = (
+                self._resolve_sys_path('product/etc/sysconfig',
+                                       'system/product/etc/sysconfig'))
         self.framework_res_apk = self._resolve_sys_path('system/framework/'
                                                         'framework-res.apk')
 
@@ -289,7 +298,8 @@
             be found.
         """
         if not apks:
-            return self._resolve_all_privapps()
+            return (self._resolve_all_system_privapps(),
+                   self._resolve_all_product_privapps())
 
         ret_apks = []
         for apk in apks:
@@ -306,29 +316,77 @@
                 raise MissingResourceError('File "%s" does not exist.' % apk)
             else:
                 ret_apks.append(apk)
-        return ret_apks
+        return ret_apks, None
 
-    def _resolve_all_privapps(self):
+    def _resolve_all_system_privapps(self):
         """Extract package name and requested permissions."""
         if self._is_android_env:
-            priv_app_dir = os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
-                                        'system/priv-app')
+            system_priv_app_dir = (
+                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+                                            'system/priv-app'))
         else:
             try:
-                priv_app_dir = self.adb.pull('/system/priv-app/')
+                system_priv_app_dir = self.adb.pull('/system/priv-app/')
             except subprocess.CalledProcessError:
                 raise MissingResourceError(
                     'Directory "/system/priv-app" could not be pulled from on '
                     'device "%s".' % self.adb.serial)
 
-        return get_output('find %s -name "*.apk"' % priv_app_dir).split()
+        return get_output('find %s -name "*.apk"' % system_priv_app_dir).split()
 
-    def _resolve_sys_path(self, file_path):
+    def _resolve_all_product_privapps(self):
+        """Extract package name and requested permissions."""
+        if self._is_android_env:
+            product_priv_app_dir = (
+                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+                                            'product/priv-app'))
+            if not os.path.exists(product_priv_app_dir):
+                product_priv_app_dir  = (
+                        os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+                                                'system/product/priv-app'))
+        else:
+            try:
+                product_priv_app_dir = self.adb.pull('/product/priv-app/')
+            except subprocess.CalledProcessError:
+                print('Warning: Directory "/product/priv-app" could not be '
+                        'pulled from on device "%s". Trying '
+                        '"/system/product/priv-app"' % self.adb.serial,
+                        file=sys.stderr)
+                try:
+                    product_priv_app_dir = (
+                            self.adb.pull('/system/product/priv-app/'))
+                except subprocess.CalledProcessError:
+                    raise MissingResourceError(
+                        'Directory "/system/product/priv-app" could not be '
+                        'pulled from on device "%s".' % self.adb.serial)
+
+        return get_output(
+                'find %s -name "*.apk"' % product_priv_app_dir).split()
+
+    def _resolve_sys_path(self, file_path, fallback_file_path=None):
         """Resolves a path that is a part of an Android System Image."""
         if self._is_android_env:
-            return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path)
+            sys_path = (
+                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path))
+            if not os.path.exists(sys_path):
+                sys_path = (
+                        os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+                        fallback_file_path))
         else:
-            return self.adb.pull(file_path)
+            try:
+                sys_path = self.adb.pull(file_path)
+            except subprocess.CalledProcessError:
+                print('Warning: Directory %s could not be pulled from on device'
+                        '"%s". Trying "/system/product/priv-app"'
+                        % (file_path, self.adb.serial), file=sys.stderr)
+                try:
+                    sys_path = self.adb.pull(fallback_file_path)
+                except subprocess.CalledProcessError:
+                    raise MissingResourceError(
+                        'Directory %s could not be pulled from on '
+                        'device "%s".' % (fallback_file_path, self.adb.serial))
+
+        return sys_path
 
 
 def get_output(command):
@@ -390,17 +448,38 @@
         help='A list of paths to priv-app APKs to generate permissions for. '
              'To make a path device-side, prefix the path with "device:".'
     )
+    parser.add_argument(
+        '-w',
+        '--writetodisk',
+        action='store_true',
+        default=False,
+        required=False,
+        help='Whether or not to store the generated permissions directly to '
+             'a file. See --systemfile/--productfile for more information.'
+    )
+    parser.add_argument(
+        '--systemfile',
+        default='./system.xml',
+        required=False,
+        help='Path to system permissions file. Default value is ./system.xml'
+    )
+    parser.add_argument(
+        '--productfile',
+        default='./product.xml',
+        required=False,
+        help='Path to system permissions file. Default value is ./product.xml'
+    )
     cmd_args = parser.parse_args()
 
     return cmd_args
 
-
-def create_permission_file(resources):
+def create_permission_file(resources, privapp_apks, permissions_dir,
+            sysconfig_dir, file=None):
     # Parse base XML files in /etc dir, permissions listed there don't have
     # to be re-added
     base_permissions = {}
-    base_xml_files = itertools.chain(list_xml_files(resources.permissions_dir),
-                                     list_xml_files(resources.sysconfig_dir))
+    base_xml_files = itertools.chain(list_xml_files(permissions_dir),
+                                     list_xml_files(sysconfig_dir))
     for xml_file in base_xml_files:
         parse_config_xml(xml_file, base_permissions)
 
@@ -409,7 +488,7 @@
 
     apps_redefine_base = []
     results = {}
-    for priv_app in resources.privapp_apks:
+    for priv_app in privapp_apks:
         pkg_info = extract_pkg_and_requested_permissions(resources.aapt,
                                                          priv_app)
         pkg_name = pkg_info['package_name']
@@ -426,7 +505,8 @@
             results[pkg_name] = sorted(priv_perms)
 
     print_xml(results, apps_redefine_base)
-
+    if file is not None:
+        print_xml(results, apps_redefine_base, file)
 
 def print_xml(results, apps_redefine_base, fd=sys.stdout):
     """Print results to the given file."""
@@ -572,7 +652,43 @@
             serial=args.serial,
             apks=args.apks
         )
-        create_permission_file(tool_resources)
+        system_permission_file=None
+        product_permission_file=None
+        print('#' * 80)
+        print('#')
+        if args.writetodisk:
+            print('#System XML written to %s:' % args.systemfile)
+            system_permission_file = open(args.systemfile, 'w')
+        else:
+            print('#System XML:')
+        print('#')
+        print('#' * 80)
+        create_permission_file(
+            tool_resources,
+            tool_resources.system_privapp_apks,
+            tool_resources.system_permissions_dir,
+            tool_resources.system_sysconfig_dir,
+            system_permission_file)
+        if args.writetodisk:
+            system_permission_file.close()
+        if tool_resources.product_privapp_apks:
+            print('#' * 80)
+            print('#')
+            if args.writetodisk:
+                print('#Product XML written to %s:' % args.productfile)
+                product_permission_file = open(args.productfile, 'w')
+            else:
+                print('#Product XML:')
+            print('#')
+            print('#' * 80)
+            create_permission_file(
+                tool_resources,
+                tool_resources.product_privapp_apks,
+                tool_resources.product_permissions_dir,
+                tool_resources.product_sysconfig_dir,
+                product_permission_file)
+            if args.writetodisk:
+                product_permission_file.close()
     except MissingResourceError as e:
         print(str(e), file=sys.stderr)
         exit(1)