Merge "Add projected texture support to evs_app"
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 83d023d..263c83a 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -139,6 +139,7 @@
 
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c24e58e..fd4a841 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -77,4 +77,8 @@
     <!--  The com.android.car.VmsPublisherService will bind to this list of clients -->
     <string-array translatable="false" name="vmsPublisherClients">
     </string-array>
+    <!--  Permissions that the com.android.car.VmsPublisherService is allowed to grant to publishers -->
+    <string-array translatable="false" name="vmsSafePermissions">
+        <item>"android.permission.ACCESS_FINE_LOCATION"</item>
+    </string-array>
 </resources>
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 8bb0167..683c1c8 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -27,9 +27,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import com.android.car.hal.VmsHalService;
@@ -37,7 +40,9 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,6 +61,7 @@
     private final Context mContext;
     private final VmsHalService mHal;
     private final VmsPublisherManager mPublisherManager;
+    private Set<String> mSafePermissions;
 
     public VmsPublisherService(Context context, VmsHalService hal) {
         mContext = context;
@@ -67,6 +73,9 @@
     @Override
     public void init() {
         mHal.addPublisherListener(this);
+        // Load permissions that can be granted to publishers.
+        mSafePermissions = new HashSet<>(
+                Arrays.asList(mContext.getResources().getStringArray(R.array.vmsSafePermissions)));
         // Launch publishers.
         String[] publisherNames = mContext.getResources().getStringArray(
                 R.array.vmsPublisherClients);
@@ -187,11 +196,12 @@
                     // Already registered, nothing to do.
                     return;
                 }
+                grantPermissions(name);
                 Intent intent = new Intent();
                 intent.setComponent(name);
                 PublisherConnection connection = new PublisherConnection();
-                if (publisherService.mContext.bindService(intent, connection,
-                        Context.BIND_AUTO_CREATE)) {
+                if (publisherService.mContext.bindServiceAsUser(intent, connection,
+                        Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
                     mPublisherConnectionMap.put(publisherName, connection);
                 } else {
                     Log.e(TAG, "unable to bind to: " + publisherName);
@@ -244,6 +254,39 @@
             mPublisherMap.clear();
         }
 
+        private void grantPermissions(ComponentName component) {
+            VmsPublisherService publisherService = mPublisherService.get();
+            if (publisherService == null) return;
+            final PackageManager packageManager = publisherService.mContext.getPackageManager();
+            final String packageName = component.getPackageName();
+            PackageInfo packageInfo;
+            try {
+                packageInfo = packageManager.getPackageInfo(packageName,
+                        PackageManager.GET_PERMISSIONS);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Error getting package info for " + packageName, e);
+                return;
+            }
+            if (packageInfo.requestedPermissions == null) return;
+            for (String permission : packageInfo.requestedPermissions) {
+                if (!publisherService.mSafePermissions.contains(permission)) {
+                    continue;
+                }
+                if (packageManager.checkPermission(permission, packageName)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    continue;
+                }
+                try {
+                    packageManager.grantRuntimePermission(packageName, permission,
+                            UserHandle.SYSTEM);
+                    Log.d(TAG, "Permission " + permission + " granted to " + packageName);
+                } catch (SecurityException | IllegalArgumentException e) {
+                    Log.e(TAG, "Error while trying to grant " + permission + " to " + packageName,
+                            e);
+                }
+            }
+        }
+
         class PublisherConnection implements ServiceConnection {
 
             private final IBinder mToken = new Binder();
diff --git a/tests/EmbeddedKitchenSinkApp/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
index 57a0620..d661902 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -14,6 +14,9 @@
 #
 #
 
+#disble build in PDK, missing ui-lib breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -47,3 +50,5 @@
 include packages/services/Car/car-support-lib/car-support.mk
 
 include $(BUILD_PACKAGE)
+
+endif #TARGET_BUILD_PDK
diff --git a/tests/VmsPublisherClientSample/Android.mk b/tests/VmsPublisherClientSample/Android.mk
index 6bb5bf7..2aa6c40 100644
--- a/tests/VmsPublisherClientSample/Android.mk
+++ b/tests/VmsPublisherClientSample/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_PRIVILEGED_MODULE := true
 
-LOCAL_CERTIFICATE := platform
+LOCAL_CERTIFICATE := testkey
 
 LOCAL_PROGUARD_ENABLED := disabled
 
diff --git a/tests/VmsPublisherClientSample/AndroidManifest.xml b/tests/VmsPublisherClientSample/AndroidManifest.xml
index d3ac195..fdc1a31 100644
--- a/tests/VmsPublisherClientSample/AndroidManifest.xml
+++ b/tests/VmsPublisherClientSample/AndroidManifest.xml
@@ -15,8 +15,11 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.google.android.car.vms.publisher"
-          android:sharedUserId="android.uid.system">
+          package="com.google.android.car.vms.publisher">
+
+    <uses-permission android:name="android.car.permission.VMS_PUBLISHER" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
 
     <uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
 
@@ -24,7 +27,8 @@
                  android:icon="@mipmap/ic_launcher"
                  android:directBootAware="true">
         <service android:name=".VmsPublisherClientSampleService"
-                 android:exported="false">
+                 android:exported="true"
+                 android:singleUser="true">
         </service>
     </application>
 </manifest>
diff --git a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
index c310464..08d37cd 100644
--- a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
+++ b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
@@ -22,7 +22,6 @@
 import android.os.Handler;
 import android.os.Message;
 
-import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -31,7 +30,7 @@
  */
 public class VmsPublisherClientSampleService extends VmsPublisherClientService {
     public static final int PUBLISH_EVENT = 0;
-    public static final VmsLayer TEST_LAYER = new VmsLayer(0,0);
+    public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
 
     private byte mCounter = 0;
     private AtomicBoolean mInitialized = new AtomicBoolean(false);
@@ -39,7 +38,7 @@
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            if (msg.what == PUBLISH_EVENT) {
+            if (msg.what == PUBLISH_EVENT && mInitialized.get()) {
                 periodicPublish();
             }
         }
@@ -51,6 +50,8 @@
      */
     @Override
     public void onVmsPublisherServiceReady() {
+        VmsSubscriptionState subscriptionState = getSubscriptions();
+        onVmsSubscriptionChange(subscriptionState);
     }
 
     @Override
@@ -64,6 +65,13 @@
         }
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mInitialized.set(false);
+        mHandler.removeMessages(PUBLISH_EVENT);
+    }
+
     private void periodicPublish() {
         publish(TEST_LAYER, new byte[]{mCounter});
         ++mCounter;
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
new file mode 100644
index 0000000..739f5d0
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.car.test;
+
+import android.annotation.ArrayRes;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+
+import com.android.car.R;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+public class VmsPublisherPermissionsTest extends MockedCarTestBase {
+    private static final String TAG = "VmsPublisherTest";
+    private static final int MOCK_PUBLISHER_LAYER_ID = 0;
+    private static final int MOCK_PUBLISHER_LAYER_VERSION = 0;
+
+    private HalHandler mHalHandler;
+    // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+    private Semaphore mHalHandlerSemaphore;
+
+    @Override
+    protected synchronized void configureMockedHal() {
+        mHalHandler = new HalHandler();
+        addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+                .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+                .setAccess(VehiclePropertyAccess.READ_WRITE)
+                .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+    }
+
+    /**
+     * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+     * contains the name of the test service defined also in this test package.
+     */
+    @Override
+    protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+        Context context = getContext()
+                .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+        Resources resources = new Resources(context.getAssets(),
+                context.getResources().getDisplayMetrics(),
+                context.getResources().getConfiguration()) {
+            @Override
+            public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+                if (id == R.array.vmsPublisherClients) {
+                    return new String[]{
+                            "com.google.android.car.vms.publisher/"
+                                    + ".VmsPublisherClientSampleService"};
+                } else if (id == R.array.vmsSafePermissions) {
+                    return new String[]{"android.permission.ACCESS_FINE_LOCATION"};
+                }
+                return super.getStringArray(id);
+            }
+        };
+        ContextWrapper wrapper = new ContextWrapper(context) {
+            @Override
+            public Resources getResources() {
+                return resources;
+            }
+        };
+        return wrapper;
+    }
+
+    private VehiclePropValue getHalSubscriptionRequest() {
+        return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+                .addIntValue(VmsMessageType.SUBSCRIBE)
+                .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+                .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+                .build();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        if (!VmsTestUtils.canRunTest(TAG)) return;
+        /**
+         * First init the semaphore, setUp will start a series of events that will ultimately
+         * update the HAL layer and release this semaphore.
+         */
+        mHalHandlerSemaphore = new Semaphore(0);
+        super.setUp();
+
+        // Inject a subscribe event which simulates the HAL is subscribed to the Sample Publisher.
+        MockedVehicleHal mHal = getMockedVehicleHal();
+        mHal.injectEvent(getHalSubscriptionRequest());
+    }
+
+    @Override
+    protected synchronized void tearDown() throws Exception {
+        if (!VmsTestUtils.canRunTest(TAG)) return;
+        super.tearDown();
+    }
+
+    /**
+     * The method setUp initializes all the Car services, including the VmsPublisherService.
+     * The VmsPublisherService will start and configure its list of clients. This list was
+     * overridden in the method getCarServiceContext.
+     * Therefore, only VmsPublisherClientSampleService will be started.
+     * The service VmsPublisherClientSampleService will publish one message, which is validated in
+     * this test.
+     */
+    public void testPermissions() throws Exception {
+        if (!VmsTestUtils.canRunTest(TAG)) return;
+        assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+        // At this point the client initialization finished. Let's validate the permissions.
+        // The VMS service is only allowed to grant ACCESS_FINE_LOCATION but not CAMERA.
+        assertTrue(
+                getContext().getPackageManager().checkPermission(
+                        "android.permission.ACCESS_FINE_LOCATION",
+                        "com.google.android.car.vms.publisher")
+                        == PackageManager.PERMISSION_GRANTED);
+        assertFalse(getContext().getPackageManager().checkPermission(
+                "android.permission.CAMERA", "com.google.android.car.vms.publisher")
+                == PackageManager.PERMISSION_GRANTED);
+    }
+
+    private class HalHandler implements VehicleHalPropertyHandler {
+        @Override
+        public synchronized void onPropertySet(VehiclePropValue value) {
+            // If this is the data message release the semaphore so the test can continue.
+            ArrayList<Integer> int32Values = value.value.int32Values;
+            if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+                    VmsMessageType.DATA) {
+                mHalHandlerSemaphore.release();
+            }
+        }
+    }
+}
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index e50627a..4b814c7 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -48,6 +48,7 @@
 ADB_CMD = "adb"
 TIMING_THRESHOLD = 5.0
 BOOT_PROP = "\[ro\.boottime\.([^\]]+)\]:\s+\[(\d+)\]"
+BOOTLOADER_TIME_PROP = "\[ro\.boot\.boottime\]:\s+\[([^\]]+)\]"
 
 max_wait_time = BOOT_TIME_TOO_BIG
 
@@ -278,7 +279,7 @@
 
   if not logcat_event_time.get(KERNEL_TIME_KEY):
     print "kernel time not captured in logcat, cannot get time diff"
-    return None, None, None
+    return None, None, None, None
   diffs = []
   diffs.append((logcat_event_time[KERNEL_TIME_KEY], logcat_event_time[KERNEL_TIME_KEY]))
   if logcat_event_time.get(BOOT_ANIM_END_TIME_KEY) and dmesg_event_time.get(BOOT_ANIM_END_TIME_KEY):
@@ -288,7 +289,7 @@
   if not dmesg_event_time.get(KERNEL_BOOT_COMPLETE):
       print "BootAnimEnd time or BootComplete-kernel not captured in both log" +\
         ", cannot get time diff"
-      return None, None, None
+      return None, None, None, None
   diffs.append((logcat_event_time[KERNEL_BOOT_COMPLETE],\
                 logcat_event_time[KERNEL_BOOT_COMPLETE] - dmesg_event_time[KERNEL_BOOT_COMPLETE]))
 
@@ -316,7 +317,7 @@
         else:
           events[k] = 0.0
 
-  data_points = {}
+  data_points = collections.OrderedDict()
 
   print "-----------------"
   print "ro.boottime.*: time"
@@ -337,9 +338,24 @@
       'from_dmesg': item[0] in replaced_from_dmesg,
       'logcat_value': logcat_original_time[item[0]]
     }
+  # add times with bootloader
+  if events.get("BootComplete") and boottime_events.get("bootloader"):
+    total = events["BootComplete"] + boottime_events["bootloader"]
+    data_points["*BootComplete+Bootloader"] = {
+      'value': total,
+      'from_dmesg': False,
+      'logcat_value': 0.0
+    }
+  if events.get("LauncherStart") and boottime_events.get("bootloader"):
+    total = events["LauncherStart"] + boottime_events["bootloader"]
+    data_points["*LauncherStart+Bootloader"] = {
+      'value': total,
+      'from_dmesg': False,
+      'logcat_value': 0.0
+    }
+  for k, v in data_points.iteritems():
     print '{0:30}: {1:<7.5} {2:1} ({3})'.format(
-      item[0], item[1], '*' if item[0] in replaced_from_dmesg else '',
-      logcat_original_time[item[0]])
+      k, v['value'], '*' if v['from_dmesg'] else '', v['logcat_value'])
 
   print '\n* - event time was obtained from dmesg log\n'
 
@@ -520,6 +536,8 @@
                              stdout=subprocess.PIPE);
   out = process.stdout
   pattern = re.compile(BOOT_PROP)
+  pattern_bootloader = re.compile(BOOTLOADER_TIME_PROP)
+  bootloader_time = 0.0
   for line in out:
     match = pattern.match(line)
     if match:
@@ -527,7 +545,18 @@
         events[match.group(1)] = float(match.group(2)) / 1000.0 #ms to s
       else:
         events[match.group(1)] = float(match.group(2)) / 1000000000.0 #ns to s
+    match = pattern_bootloader.match(line)
+    if match:
+      items = match.group(1).split(",")
+      for item in items:
+        entry_pair = item.split(":")
+        entry_name = entry_pair[0]
+        time_spent = float(entry_pair[1]) / 1000 #ms to s
+        if entry_name != "SW":
+          bootloader_time = bootloader_time + time_spent
   ordered_event = collections.OrderedDict()
+  if bootloader_time != 0.0:
+    ordered_event["bootloader"] = bootloader_time;
   for item in sorted(events.items(), key=operator.itemgetter(1)):
     ordered_event[item[0]] = item[1]
   return ordered_event
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml
index 46b2d21..923ca5d 100644
--- a/tools/bootanalyze/config.yaml
+++ b/tools/bootanalyze/config.yaml
@@ -50,6 +50,6 @@
   KeyguardShown: KeyguardServiceDelegate.*\*\*\*\* SHOWN CALLED \*\*\*\*
   BootComplete: Starting phase 1000
   BootComplete_kernel: processing action \(sys\.boot_completed=1\)
-  LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity)
+  LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity|SetupWizardActivity)
   FsStat: fs_stat, partition:userdata stat:(0x\S+)