Final improvements on CarProvision app:

- Listen to Car UX restrictions so it exits if the car moves or
  gear changes to rear.
- Sets the proper car-related Settings.
- Don't disable the DeviceOwner button if the DPC app is not
  available (so it can be installed through adb)
- Exit right away on headless system user.
- Removed "Cancel Setup" button.

Test: m -j CarProvision && adb sync && \
      adb shell am force-stop com.android.car.provision && \
      adb shell pm enable --user cur com.android.car.provision && \
      adb shell pm enable --user cur com.android.car.provision/.DefaultActivity && \
      adb shell am start -a android.intent.action.MAIN \
        -c android.intent.category.HOME -c android.intent.category.DEFAULT \
        -c android.intent.category.SETUP_WIZARD

Fixes: 170143095
Bug: 171066617

Change-Id: I8147676d66f207a832d549b23d5f0394b93904f0
diff --git a/Android.bp b/Android.bp
index fd42623..4e2c402 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,12 @@
 android_app {
     name: "CarProvision",
     srcs: ["**/*.java"],
+    libs: [
+        "android.car-stubs",
+    ],
+    static_libs: [
+        "car-setup-wizard-lib",
+    ],
     platform_apis: true,
     system_ext_specific: true,
     certificate: "platform",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 69d3ba5..fc9507d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,6 +26,12 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
 
+    <!-- To listen to UX restriction events -->
+    <uses-permission android:name="android.car.permission.CAR_POWERTRAIN"/>
+
+    <!-- To show the exited setup notification -->
+    <uses-permission android:name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
+
     <application android:label="@string/app_name">
 
         <activity android:name="DefaultActivity"
diff --git a/res/drawable-hdpi/car_ic_mode.png b/res/drawable-hdpi/car_ic_mode.png
new file mode 100644
index 0000000..a8f719f
--- /dev/null
+++ b/res/drawable-hdpi/car_ic_mode.png
Binary files differ
diff --git a/res/layout/default_activity.xml b/res/layout/default_activity.xml
index 0262dce..3850c22 100644
--- a/res/layout/default_activity.xml
+++ b/res/layout/default_activity.xml
@@ -39,7 +39,7 @@
             android:id="@+id/error_message"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"/>
-  </LinearLayout>
+    </LinearLayout>
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
@@ -51,11 +51,6 @@
             android:text="@string/set_do"
             android:enabled="false"/>
         <Button
-            android:id="@+id/cancel_setup"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/cancel_setup"/>
-        <Button
             android:id="@+id/finish_setup"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1e34891..24e8a7c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -18,11 +18,13 @@
     <string name="app_name" translatable="false">Reference Setup Wizard for Cars</string>
     <string name="description" translatable="false">This app is used to emulate a Setup Wizard.\n
     \n
-    It is integrated with car-specific features (such as deferred setup and user notice service)
+    It\'s integrated with car-specific features (such as exiting setup when car moves)
     plus a UI where the user can emulate setup-time options like provisioning a Device Owner.
     </string>
     <string name="error_prompt" translatable="false">Error:</string>
-    <string name="cancel_setup" translatable="false">Cancel Setup</string>
     <string name="finish_setup" translatable="false">Finish Setup</string>
     <string name="set_do" translatable="false">Set Device Owner</string>
+    <string name="exited_setup_title" translatable="false">Setup Exited</string>
+    <string name="exited_setup_content" translatable="false">Don\'t worry, this is just a friendly
+    FYI, there\'s nothing to do, you are good to go.\nHave fun!</string>
 </resources>
diff --git a/src/com/android/car/provision/DefaultActivity.java b/src/com/android/car/provision/DefaultActivity.java
index 8486029..6dd7bdb 100644
--- a/src/com/android/car/provision/DefaultActivity.java
+++ b/src/com/android/car/provision/DefaultActivity.java
@@ -17,19 +17,28 @@
 package com.android.car.provision;
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
 
+import com.android.car.setupwizardlib.util.CarDrivingStateMonitor;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -40,19 +49,26 @@
  *
  * <ul>
  *   <li>Shows UI where user can confirm setup.
- *   <li>Listen to UX restriction events, so it defers setup when the car moves.
+ *   <li>Listen to UX restriction events, so it exits setup when the car moves.
  *   <li>Add option to setup DeviceOwner mode.
  *   <li>Sets car-specific properties.
  * </ul>
  */
 public final class DefaultActivity extends Activity {
 
-    // TODO(b/170957342): implement the features mentioned above
+    static final String TAG = "CarProvision";
 
-    private static final String TAG = "CarProvision";
+    // TODO(b/171066617): copied from android.car.settings.CarSettings, as they're hidden
+    private static final String KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER =
+            "android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER";
+    private static final String KEY_SETUP_WIZARD_IN_PROGRESS =
+            "android.car.SETUP_WIZARD_IN_PROGRESS";
 
     private static final int REQUEST_CODE_SET_DO = 42;
 
+    private static final int NOTIFICATION_ID = 108;
+    private static final String IMPORTANCE_DEFAULT_ID = "importance_default";
+
     private static final Map<String, String> sSupportedDpcApps = new HashMap<>(1);
 
     static {
@@ -61,27 +77,70 @@
                 "com.afwsamples.testdpc.SetupManagementLaunchActivity");
     }
 
+    private CarDrivingStateMonitor mCarDrivingStateMonitor;
+
     private TextView mErrorsTextView;
-    private Button mCancelSetupButton;
     private Button mFinishSetupButton;
     private Button mDoProvisioningButton;
 
+    private final BroadcastReceiver mDrivingStateExitReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "onReceive(): " + intent);
+            exitSetup();
+        }
+    };
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        Log.i(TAG, "onCreate() for user " + getUserId());
 
+        int userId = getUserId();
+        Log.i(TAG, "onCreate() for user " + userId + " Intent: " + getIntent());
+
+        if (userId == UserHandle.USER_SYSTEM && UserManager.isHeadlessSystemUserMode()) {
+            finishSetup();
+            return;
+        }
+
+        setCarSetupInProgress(true);
         setContentView(R.layout.default_activity);
 
         mErrorsTextView = findViewById(R.id.error_message);
-        mCancelSetupButton = findViewById(R.id.cancel_setup);
         mFinishSetupButton = findViewById(R.id.finish_setup);
         mDoProvisioningButton = findViewById(R.id.do_provisioning);
 
-        mCancelSetupButton.setOnClickListener((v) -> cancelSetup());
         mFinishSetupButton.setOnClickListener((v) -> finishSetup());
 
         setDoProvisioning();
+        startMonitor();
+    }
+
+    private void startMonitor() {
+        registerReceiver(mDrivingStateExitReceiver,
+                new IntentFilter(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION));
+
+        mCarDrivingStateMonitor = CarDrivingStateMonitor.get(this);
+        mCarDrivingStateMonitor.startMonitor();
+    }
+
+    @Override
+    public void finish() {
+        Log.i(TAG, "finish() for user " + getUserId());
+
+        stopMonitor();
+
+        super.finish();
+    };
+
+    private void stopMonitor() {
+        if (mDrivingStateExitReceiver != null) {
+            unregisterReceiver(mDrivingStateExitReceiver);
+        }
+
+        if (mCarDrivingStateMonitor != null) {
+            mCarDrivingStateMonitor.stopMonitor();
+        }
     }
 
     private void setDoProvisioning() {
@@ -98,13 +157,6 @@
             return;
         }
 
-        // TODO(b/170143095): populate UI with supported options instead
-        String dpcApp = sSupportedDpcApps.keySet().iterator().next();
-        if (!checkDpcAppExists(dpcApp)) {
-            Log.i(TAG, "Disabling DeviceOwner buttom because device does not have any DPC app");
-            return;
-        }
-
         mDoProvisioningButton.setEnabled(true);
         mDoProvisioningButton.setOnClickListener((v) -> provisionDeviceOwner());
     }
@@ -136,28 +188,59 @@
 
     private void finishSetup() {
         Log.i(TAG, "finishing setup for user " + getUserId());
+        provisionUserAndDevice();
+        disableSelfAndFinish();
+    }
+
+    private void provisionUserAndDevice() {
+        Log.d(TAG, "setting Settings properties");
         // Add a persistent setting to allow other apps to know the device has been provisioned.
         Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
         Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
 
-        // TODO(b/170143095): set android.car.SETUP_WIZARD_IN_PROGRESS and
-        // android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER as well
-
-        // TODO(b/170143095): listen to driving safety restriction events
-
-        disableSelf();
-
-        // Terminate the activity.
-        finish();
+        // Set car-specific properties
+        setCarSetupInProgress(false);
+        Settings.Secure.putInt(getContentResolver(), KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, 0);
     }
 
-    private void cancelSetup() {
-        Log.i(TAG, "cancelling setup for user " + getUserId());
-        finish();
+    private void setCarSetupInProgress(boolean inProgress) {
+        Settings.Secure.putInt(getContentResolver(), KEY_SETUP_WIZARD_IN_PROGRESS,
+                inProgress ? 1 : 0);
+    }
+
+    private void exitSetup() {
+        Log.d(TAG, "exitSetup()");
+        provisionUserAndDevice();
+        notifySetupExited();
+        disableSelfAndFinish();
+    }
+
+    private void notifySetupExited() {
+        Log.d(TAG, "Sending exited setup notification");
+
+        NotificationManager notificationMgr = getSystemService(NotificationManager.class);
+        notificationMgr.createNotificationChannel(new NotificationChannel(
+                IMPORTANCE_DEFAULT_ID, "Importance Default",
+                NotificationManager.IMPORTANCE_DEFAULT));
+        Notification notification = new Notification
+                .Builder(this, IMPORTANCE_DEFAULT_ID)
+                .setContentTitle(getString(R.string.exited_setup_title))
+                .setContentText(getString(R.string.exited_setup_content))
+                .setCategory(Notification.CATEGORY_CAR_INFORMATION)
+                .setSmallIcon(R.drawable.car_ic_mode)
+                .build();
+        notificationMgr.notify(NOTIFICATION_ID, notification);
     }
 
     private void provisionDeviceOwner() {
         // TODO(b/170957342): add a UI with multiple options once AAOS provides a CarTestDPC app.
+        String dpcApp = sSupportedDpcApps.keySet().iterator().next();
+        if (!checkDpcAppExists(dpcApp)) {
+            showErrorMessage("Cannot setup DeviceOwner because " + dpcApp + " is not available.\n"
+                    + "Make sure it's installed for both user 0 and user " + getUserId());
+            return;
+        }
+
         Intent intent = new Intent();
         Map.Entry<String, String> dpc = sSupportedDpcApps.entrySet().iterator().next();
         intent.setComponent(new ComponentName(dpc.getKey(), dpc.getValue()));
@@ -166,13 +249,15 @@
         startActivityForResult(intent, REQUEST_CODE_SET_DO);
     }
 
-    private void disableSelf() {
+    private void disableSelfAndFinish() {
         // Remove this activity from the package manager.
         PackageManager pm = getPackageManager();
         ComponentName name = new ComponentName(this, DefaultActivity.class);
         Log.i(TAG, "Disabling itself (" + name + ") for user " + getUserId());
         pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                 PackageManager.DONT_KILL_APP);
+
+        finish();
     }
 
     @Override
@@ -189,7 +274,7 @@
             return;
         }
         Log.i(TAG, "Device owner  mode provisioned, nothing left to do...");
-        disableSelf();
+        disableSelfAndFinish();
     };
 
     private static String resultCodeToString(int resultCode)  {