Merge "Create startForeground option in VendorServiceController." into qt-dev
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 4aef4a5..a81bed4 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -155,12 +155,14 @@
          Every item in this array contains a flatten component name of a service that needs to be
          started and a list of parameters after hashtag symbol. Here's the format:
 
-         <item>com.bar.foo/.Service#bind={true|false},user={all|system|foreground},trigger={asap,userUnlocked}</item>
+         <item>com.bar.foo/.Service#bind={bind|start|startForeground},user={all|system|foreground},
+         trigger={asap,userUnlocked}</item>
 
-         bind: indicates whether service needs to be bound or started, if the value is 'true'
-               the service will be bound by calling Context#bindService(...), otherwise it will
-               be started using Context#startService. If service was bound it will be restarted
-               unless it is constantly crashing. The default value is 'false'
+         bind: bind - start service with Context#bindService
+               start - start service with Context#startService
+               startForeground - start service with Context#startForegroundService
+               If service was bound it will be restarted unless it is constantly crashing.
+               The default value is 'start'
          user: all - the service will be bound/started for system and all foreground users
                system - the service will be started/bound only for system user (u0)
                foreground - the service will be bound/started only for foreground users
@@ -175,7 +177,7 @@
          is no longer foreground.
      -->
     <string-array translatable="false" name="config_earlyStartupServices">
-        <!-- list your services here -->
+        <item>com.android.car.messenger/.MessengerService#bind=startForeground,user=foreground,trigger=userUnlocked</item>
     </string-array>
 
     <string name="config_TetheredProjectionAccessPointSsid" translatable="false">CarAP</string>
diff --git a/service/src/com/android/car/pm/VendorServiceController.java b/service/src/com/android/car/pm/VendorServiceController.java
index 0c8eae3..189370e 100644
--- a/service/src/com/android/car/pm/VendorServiceController.java
+++ b/service/src/com/android/car/pm/VendorServiceController.java
@@ -300,6 +300,9 @@
             Intent intent = mVendorServiceInfo.getIntent();
             if (mVendorServiceInfo.shouldBeBound()) {
                 return mContext.bindServiceAsUser(intent, this, BIND_AUTO_CREATE, mHandler, mUser);
+            } else if (mVendorServiceInfo.shouldBeStartedInForeground()) {
+                mStarted = mContext.startForegroundServiceAsUser(intent, mUser) != null;
+                return mStarted;
             } else {
                 mStarted = mContext.startServiceAsUser(intent, mUser) != null;
                 return mStarted;
diff --git a/service/src/com/android/car/pm/VendorServiceInfo.java b/service/src/com/android/car/pm/VendorServiceInfo.java
index 94a50a0..ba79978 100644
--- a/service/src/com/android/car/pm/VendorServiceInfo.java
+++ b/service/src/com/android/car/pm/VendorServiceInfo.java
@@ -53,17 +53,28 @@
     })
     @interface Trigger {}
 
-    private final boolean mBind;
+    private static final int BIND = 0;
+    private static final int START = 1;
+    private static final int START_FOREGROUND = 2;
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            BIND,
+            START,
+            START_FOREGROUND,
+    })
+    @interface Bind {}
+
+    private final @Bind int mBind;
     private final @UserScope int mUserScope;
     private final @Trigger int mTrigger;
     private final ComponentName mComponentName;
 
-    private VendorServiceInfo(ComponentName componentName, boolean bind, @UserScope int userScope,
+    private VendorServiceInfo(ComponentName componentName, @Bind int bind, @UserScope int userScope,
             @Trigger int trigger) {
         mComponentName = componentName;
-        mBind = bind;
         mUserScope = userScope;
         mTrigger = trigger;
+        mBind = bind;
     }
 
     boolean isSystemUserService() {
@@ -83,7 +94,11 @@
     }
 
     boolean shouldBeBound() {
-        return mBind;
+        return mBind == BIND;
+    }
+
+    boolean shouldBeStartedInForeground() {
+        return mBind == START_FOREGROUND;
     }
 
     Intent getIntent() {
@@ -102,11 +117,11 @@
 
         final ComponentName cn = ComponentName.unflattenFromString(serviceParamTokens[0]);
         if (cn == null) {
-            throw new IllegalArgumentException("Failed to parse service info: "
-                    + rawServiceInfo + ", expected a single '#' symbol");
+            throw new IllegalArgumentException("Failed to unflatten component name from: "
+                    + rawServiceInfo);
         }
 
-        boolean bind = false;
+        int bind = START;
         int userScope = USER_SCOPE_ALL;
         int trigger = TRIGGER_UNLOCKED;
 
@@ -121,7 +136,15 @@
 
                 switch (key) {
                     case KEY_BIND:
-                        bind = TextUtils.equals("true", val.toLowerCase());
+                        if ("bind".equals(val)) {
+                            bind = BIND;
+                        } else if ("start".equals(val)) {
+                            bind = START;
+                        } else if ("startForeground".equals(val)) {
+                            bind = START_FOREGROUND;
+                        } else {
+                            throw new IllegalArgumentException("Unexpected bind option: " + val);
+                        }
                         break;
                     case KEY_USER_SCOPE:
                         if ("all".equals(val)) {
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
index 3045276..8174add 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
@@ -43,7 +43,7 @@
     @Test
     public void multipleHashTags() {
         exception.expect(IllegalArgumentException.class);
-        VendorServiceInfo.parse(SERVICE_NAME + "#user=system#bind=true");
+        VendorServiceInfo.parse(SERVICE_NAME + "#user=system#bind=bind");
     }
 
     @Test
@@ -64,6 +64,7 @@
         assertThat(info.getIntent().getComponent())
                 .isEqualTo(ComponentName.unflattenFromString(SERVICE_NAME));
         assertThat(info.shouldBeBound()).isFalse();
+        assertThat(info.shouldBeStartedInForeground()).isFalse();
         assertThat(info.isSystemUserService()).isTrue();
         assertThat(info.isForegroundUserService()).isTrue();
         assertThat(info.shouldStartOnUnlock()).isTrue();
@@ -71,16 +72,25 @@
 
     @Test
     public void startService() {
-        VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME + "#bind=false");
+        VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME + "#bind=start");
         assertThat(info.shouldBeBound()).isFalse();
+        assertThat(info.shouldBeStartedInForeground()).isFalse();
         assertThat(info.getIntent().getComponent())
                 .isEqualTo(ComponentName.unflattenFromString(SERVICE_NAME));
     }
 
     @Test
     public void bindService() {
-        VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME + "#bind=true");
+        VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME + "#bind=bind");
         assertThat(info.shouldBeBound()).isTrue();
+        assertThat(info.shouldBeStartedInForeground()).isFalse();
+    }
+
+    @Test
+    public void startServiceInForeground() {
+        VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME + "#bind=startForeground");
+        assertThat(info.shouldBeBound()).isFalse();
+        assertThat(info.shouldBeStartedInForeground()).isTrue();
     }
 
     @Test
@@ -131,7 +141,7 @@
     @Test
     public void allArgs() {
         VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME
-                + "#bind=true,user=foreground,trigger=userUnlocked");
+                + "#bind=bind,user=foreground,trigger=userUnlocked");
         assertThat(info.getIntent().getComponent())
                 .isEqualTo(ComponentName.unflattenFromString(SERVICE_NAME));
         assertThat(info.shouldBeBound()).isTrue();