Merge "Added callback to enable CarSysUI for showing user switch dialog during before user switching" into rvc-dev
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 3160e9e..099d56a 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -988,6 +988,10 @@
 
     /**
      * A factory method that creates Car instance for all Car API access.
+     *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context App's Context. This should not be null. If you are passing
      *                {@link ContextWrapper}, make sure that its base Context is non-null as well.
      *                Otherwise it will throw {@link java.lang.NullPointerException}.
@@ -1020,6 +1024,9 @@
      * A factory method that creates Car instance for all Car API access using main thread {@code
      * Looper}.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @see #createCar(Context, ServiceConnection, Handler)
      *
      * @deprecated use {@link #createCar(Context, Handler)} instead.
@@ -1032,6 +1039,9 @@
     /**
      * Creates new {@link Car} object which connected synchronously to Car Service and ready to use.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context application's context
      *
      * @return Car object if operation succeeded, otherwise null.
@@ -1044,6 +1054,9 @@
     /**
      * Creates new {@link Car} object which connected synchronously to Car Service and ready to use.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * @param context App's Context. This should not be null. If you are passing
      *                {@link ContextWrapper}, make sure that its base Context is non-null as well.
      *                Otherwise it will throw {@link java.lang.NullPointerException}.
@@ -1111,6 +1124,9 @@
     /**
      * Creates new {@link Car} object with {@link CarServiceLifecycleListener}.
      *
+     * <p>Instance created with this should be disconnected from car service by calling
+     * {@link #disconnect()} before the passed {code Context} is released.
+     *
      * <p> If car service is ready inside this call and if the caller is running in the main thread,
      * {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} will be called
      * with ready set to be true. Otherwise,
diff --git a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
index b41c7de..444f82a 100644
--- a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
@@ -57,25 +57,31 @@
 
     private final Bundle mExtra;
 
-    public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR
-            = new Parcelable.Creator<CarNavigationInstrumentCluster>() {
-        public CarNavigationInstrumentCluster createFromParcel(Parcel in) {
-            return new CarNavigationInstrumentCluster(in);
-        }
+    public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR =
+                new Parcelable.Creator<CarNavigationInstrumentCluster>() {
+            public CarNavigationInstrumentCluster createFromParcel(Parcel in) {
+                return new CarNavigationInstrumentCluster(in);
+            }
 
-        public CarNavigationInstrumentCluster[] newArray(int size) {
-            return new CarNavigationInstrumentCluster[size];
-        }
-    };
+            public CarNavigationInstrumentCluster[] newArray(int size) {
+                return new CarNavigationInstrumentCluster[size];
+            }
+        };
 
+    /**
+     * Creates a new {@link CarNavigationInstrumentCluster}.
+     */
     public static CarNavigationInstrumentCluster createCluster(int minIntervalMillis) {
         return new CarNavigationInstrumentCluster(minIntervalMillis, CLUSTER_TYPE_IMAGE_CODES_ONLY,
                 0, 0, 0);
     }
 
-    public static CarNavigationInstrumentCluster createCustomImageCluster(int minIntervalMs,
+    /**
+     * Creates a new {@link CarNavigationInstrumentCluster}.
+     */
+    public static CarNavigationInstrumentCluster createCustomImageCluster(int minIntervalMillis,
             int imageWidth, int imageHeight, int imageColorDepthBits) {
-        return new CarNavigationInstrumentCluster(minIntervalMs,
+        return new CarNavigationInstrumentCluster(minIntervalMillis,
                 CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED,
                 imageWidth, imageHeight, imageColorDepthBits);
     }
@@ -108,7 +114,9 @@
      * Contains extra information about instrument cluster.
      * @hide
      */
-    public Bundle getExtra() { return mExtra; }
+    public Bundle getExtra() {
+        return mExtra;
+    }
 
     /**
      * If instrument cluster is image, number of bits of colour depth it supports (8, 16, or 32).
@@ -127,10 +135,9 @@
 
     /**
      * Whether cluster support custom image or not.
-     * @return
      */
     public boolean supportsCustomImages() {
-      return mType == CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED;
+        return mType == CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED;
     }
 
     private CarNavigationInstrumentCluster(
@@ -174,12 +181,12 @@
     /** Converts to string for debug purpose */
     @Override
     public String toString() {
-        return CarNavigationInstrumentCluster.class.getSimpleName() + "{ " +
-                "minIntervalMillis: " + mMinIntervalMillis + ", " +
-                "type: " + mType + ", " +
-                "imageWidth: " + mImageWidth + ", " +
-                "imageHeight: " + mImageHeight + ", " +
-                "imageColourDepthBits: " + mImageColorDepthBits +
-                "extra: " + mExtra + " }";
+        return CarNavigationInstrumentCluster.class.getSimpleName() + "{ "
+                + "minIntervalMillis: " + mMinIntervalMillis + ", "
+                + "type: " + mType + ", "
+                + "imageWidth: " + mImageWidth + ", "
+                + "imageHeight: " + mImageHeight + ", "
+                + "imageColourDepthBits: " + mImageColorDepthBits
+                + "extra: " + mExtra + " }";
     }
 }
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index b901ec2..2ae56cb 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -36,6 +36,7 @@
 import android.car.user.GetUserIdentificationAssociationResponse;
 import android.car.user.UserSwitchResult;
 import android.car.userlib.CarUserManagerHelper;
+import android.car.userlib.CommonConstants.CarUserServiceConstants;
 import android.car.userlib.HalCallback;
 import android.car.userlib.UserHalHelper;
 import android.content.Context;
@@ -103,13 +104,14 @@
     private static final String TAG = TAG_USER;
 
     /** {@code int} extra used to represent a user id in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_ID = "user.id";
+    public static final String BUNDLE_USER_ID = CarUserServiceConstants.BUNDLE_USER_ID;
     /** {@code int} extra used to represent user flags in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_FLAGS = "user.flags";
+    public static final String BUNDLE_USER_FLAGS = CarUserServiceConstants.BUNDLE_USER_FLAGS;
     /** {@code String} extra used to represent a user name in a {@link IResultReceiver} response. */
-    public static final String BUNDLE_USER_NAME = "user.name";
+    public static final String BUNDLE_USER_NAME = CarUserServiceConstants.BUNDLE_USER_NAME;
     /** {@code int} extra used to represent the info action {@link IResultReceiver} response. */
-    public static final String BUNDLE_INITIAL_INFO_ACTION = "initial_info.action";
+    public static final String BUNDLE_INITIAL_INFO_ACTION =
+            CarUserServiceConstants.BUNDLE_INITIAL_INFO_ACTION;
 
     private final Context mContext;
     private final CarUserManagerHelper mCarUserManagerHelper;
diff --git a/user/car-user-lib/src/android/car/userlib/CommonConstants.java b/user/car-user-lib/src/android/car/userlib/CommonConstants.java
new file mode 100644
index 0000000..ef9a354
--- /dev/null
+++ b/user/car-user-lib/src/android/car/userlib/CommonConstants.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 android.car.userlib;
+
+/**
+ * Provides constants used by both CarService and CarServiceHelper packages.
+ */
+public final class CommonConstants {
+
+    private CommonConstants() {
+        throw new UnsupportedOperationException("contains only static constants");
+    }
+
+    /**
+     * Constants used on {@link android.os.Bundle bundles} sent by
+     * {@link com.android.car.user.CarUserService} on binder calls.
+     */
+    public static final class CarUserServiceConstants {
+
+        public static final String BUNDLE_USER_ID = "user.id";
+        public static final String BUNDLE_USER_FLAGS = "user.flags";
+        public static final String BUNDLE_USER_NAME = "user.name";
+        public static final String BUNDLE_INITIAL_INFO_ACTION = "initial_info.action";
+
+        private CarUserServiceConstants() {
+            throw new UnsupportedOperationException("contains only static constants");
+        }
+    }
+}
diff --git a/watchdog/server/src/IoPerfCollection.cpp b/watchdog/server/src/IoPerfCollection.cpp
index a97858b..2799417 100644
--- a/watchdog/server/src/IoPerfCollection.cpp
+++ b/watchdog/server/src/IoPerfCollection.cpp
@@ -55,6 +55,7 @@
 using android::base::Result;
 using android::base::Split;
 using android::base::StringAppendF;
+using android::base::StringPrintf;
 using android::base::WriteStringToFd;
 using android::content::pm::IPackageManagerNative;
 
@@ -76,6 +77,24 @@
 
 const std::string kDumpMajorDelimiter = std::string(100, '-') + "\n";
 
+constexpr const char* kHelpText =
+        "\nCustom I/O performance data collection dump options:\n"
+        "%s: Starts custom I/O performance data collection. Customize the collection behavior with "
+        "the following optional arguments:\n"
+        "\t%s <seconds>: Modifies the collection interval. Default behavior is to collect once "
+        "every %lld seconds.\n"
+        "\t%s <seconds>: Modifies the maximum collection duration. Default behavior is to collect "
+        "until %ld minutes before automatically stopping the custom collection and discarding "
+        "the collected data.\n"
+        "\t%s <package name>,<package, name>,...: Comma-separated value containing package names. "
+        "When provided, the results are filtered only to the provided package names. Default "
+        "behavior is to list the results for the top %d packages.\n"
+        "%s: Stops custom I/O performance data collection and generates a dump of "
+        "the collection report.\n\n"
+        "When no options are specified, the carwatchdog report contains the I/O performance "
+        "data collected during boot-time and over the last %ld minutes before the report "
+        "generation.";
+
 double percentage(uint64_t numer, uint64_t denom) {
     return denom == 0 ? 0.0 : (static_cast<double>(numer) / static_cast<double>(denom)) * 100.0;
 }
@@ -406,8 +425,8 @@
 
     if (args[0] == String16(kStartCustomCollectionFlag)) {
         if (args.size() > 7) {
-            return Error(INVALID_OPERATION) << "Number of arguments to start custom "
-                                            << "I/O performance data collection cannot exceed 7";
+            return Error(BAD_VALUE) << "Number of arguments to start custom I/O performance data "
+                                    << "collection cannot exceed 7";
         }
         std::chrono::nanoseconds interval = kCustomCollectionInterval;
         std::chrono::nanoseconds maxDuration = kCustomCollectionDuration;
@@ -416,7 +435,7 @@
             if (args[i] == String16(kIntervalFlag)) {
                 const auto& ret = parseSecondsFlag(args, i + 1);
                 if (!ret) {
-                    return Error(FAILED_TRANSACTION)
+                    return Error(BAD_VALUE)
                             << "Failed to parse " << kIntervalFlag << ": " << ret.error();
                 }
                 interval = std::chrono::duration_cast<std::chrono::nanoseconds>(*ret);
@@ -426,7 +445,7 @@
             if (args[i] == String16(kMaxDurationFlag)) {
                 const auto& ret = parseSecondsFlag(args, i + 1);
                 if (!ret) {
-                    return Error(FAILED_TRANSACTION)
+                    return Error(BAD_VALUE)
                             << "Failed to parse " << kMaxDurationFlag << ": " << ret.error();
                 }
                 maxDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(*ret);
@@ -435,7 +454,7 @@
             }
             if (args[i] == String16(kFilterPackagesFlag)) {
                 if (args.size() < i + 1) {
-                    return Error(FAILED_TRANSACTION)
+                    return Error(BAD_VALUE)
                             << "Must provide value for '" << kFilterPackagesFlag << "' flag";
                 }
                 std::vector<std::string> packages =
@@ -447,12 +466,13 @@
             }
             ALOGW("Unknown flag %s provided to start custom I/O performance data collection",
                   String8(args[i]).string());
-            return Error(INVALID_OPERATION) << "Unknown flag " << String8(args[i]).string()
-                                            << " provided to start custom I/O performance data "
-                                            << "collection";
+            return Error(BAD_VALUE) << "Unknown flag " << String8(args[i]).string()
+                                    << " provided to start custom I/O performance data "
+                                    << "collection";
         }
         const auto& ret = startCustomCollection(interval, maxDuration, filterPackages);
         if (!ret) {
+            WriteStringToFd(ret.error().message(), fd);
             return ret;
         }
         return {};
@@ -460,19 +480,37 @@
 
     if (args[0] == String16(kEndCustomCollectionFlag)) {
         if (args.size() != 1) {
-            ALOGW("Number of arguments to end custom I/O performance data collection cannot "
-                  "exceed 1");
+            ALOGW("Number of arguments to stop custom I/O performance data collection cannot "
+                  "exceed 1. Stopping the data collection.");
+            WriteStringToFd("Number of arguments to stop custom I/O performance data collection "
+                            "cannot exceed 1. Stopping the data collection.",
+                            fd);
         }
-        const auto& ret = endCustomCollection(fd);
-        if (!ret) {
-            return ret;
-        }
-        return {};
+        return endCustomCollection(fd);
     }
 
-    return Error(INVALID_OPERATION)
-            << "Dump arguments start neither with " << kStartCustomCollectionFlag << " nor with "
-            << kEndCustomCollectionFlag << " flags";
+    return Error(BAD_VALUE) << "I/O perf collection dump arguments start neither with "
+                            << kStartCustomCollectionFlag << " nor with "
+                            << kEndCustomCollectionFlag << " flags";
+}
+
+bool IoPerfCollection::dumpHelpText(int fd) {
+    long periodicCacheMinutes =
+            (std::chrono::duration_cast<std::chrono::seconds>(mPeriodicCollection.interval)
+                     .count() *
+             mPeriodicCollection.maxCacheSize) /
+            60;
+    return WriteStringToFd(StringPrintf(kHelpText, kStartCustomCollectionFlag, kIntervalFlag,
+                                        std::chrono::duration_cast<std::chrono::seconds>(
+                                                kCustomCollectionInterval)
+                                                .count(),
+                                        kMaxDurationFlag,
+                                        std::chrono::duration_cast<std::chrono::minutes>(
+                                                kCustomCollectionDuration)
+                                                .count(),
+                                        kFilterPackagesFlag, mTopNStatsPerCategory,
+                                        kEndCustomCollectionFlag, periodicCacheMinutes),
+                           fd);
 }
 
 Result<void> IoPerfCollection::dumpCollection(int fd) {
diff --git a/watchdog/server/src/IoPerfCollection.h b/watchdog/server/src/IoPerfCollection.h
index 9cde199..4cccb05 100644
--- a/watchdog/server/src/IoPerfCollection.h
+++ b/watchdog/server/src/IoPerfCollection.h
@@ -190,6 +190,9 @@
     // Returns any error observed during the dump generation.
     virtual android::base::Result<void> dump(int fd, const Vector<String16>& args);
 
+    // Dumps the help text.
+    bool dumpHelpText(int fd);
+
 private:
     // Generates a dump from the boot-time and periodic collection events.
     android::base::Result<void> dumpCollection(int fd);
diff --git a/watchdog/server/src/WatchdogBinderMediator.cpp b/watchdog/server/src/WatchdogBinderMediator.cpp
index da4ea29..0a482fd 100644
--- a/watchdog/server/src/WatchdogBinderMediator.cpp
+++ b/watchdog/server/src/WatchdogBinderMediator.cpp
@@ -18,6 +18,7 @@
 
 #include "WatchdogBinderMediator.h"
 
+#include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android/automotive/watchdog/BootPhase.h>
@@ -38,10 +39,19 @@
 using android::base::ParseUint;
 using android::base::Result;
 using android::base::StringPrintf;
+using android::base::WriteStringToFd;
 using android::binder::Status;
 
 namespace {
 
+constexpr const char* kHelpFlag = "--help";
+constexpr const char* kHelpShortFlag = "-h";
+constexpr const char* kHelpText =
+        "CarWatchdog daemon dumpsys help page:\n"
+        "Format: dumpsys android.automotive.watchdog.ICarWatchdog/default [options]\n\n"
+        "%s or %s: Displays this help text.\n"
+        "When no options are specified, carwatchdog report is generated.\n";
+
 Status checkSystemPermission() {
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
         return Status::fromExceptionCode(Status::EX_SECURITY,
@@ -94,21 +104,48 @@
         }
         return OK;
     }
+
+    if (args[0] == String16(kHelpFlag) || args[0] == String16(kHelpShortFlag)) {
+        if (!dumpHelpText(fd, "")) {
+            ALOGW("Failed to write help text to fd");
+            return FAILED_TRANSACTION;
+        }
+        return OK;
+    }
+
     if (args[0] == String16(kStartCustomCollectionFlag) ||
         args[0] == String16(kEndCustomCollectionFlag)) {
         auto ret = mIoPerfCollection->dump(fd, args);
-        std::string mode = args[0] == String16(kStartCustomCollectionFlag) ? "start" : "end";
         if (!ret.ok()) {
-            ALOGW("Failed to %s custom I/O perf collection: %s", mode.c_str(),
-                  ret.error().message().c_str());
+            std::string mode = args[0] == String16(kStartCustomCollectionFlag) ? "start" : "end";
+            std::string errorMsg = StringPrintf("Failed to %s custom I/O perf collection: %s",
+                                                mode.c_str(), ret.error().message().c_str());
+            if (ret.error().code() == BAD_VALUE) {
+                dumpHelpText(fd, errorMsg);
+            } else {
+                ALOGW("%s", errorMsg.c_str());
+            }
             return ret.error().code();
         }
         return OK;
     }
-    ALOGW("Invalid dump arguments");
+    dumpHelpText(fd, "Invalid dump arguments");
     return INVALID_OPERATION;
 }
 
+bool WatchdogBinderMediator::dumpHelpText(int fd, std::string errorMsg) {
+    if (!errorMsg.empty()) {
+        ALOGW("Error: %s", errorMsg.c_str());
+        if (!WriteStringToFd(StringPrintf("Error: %s\n\n", errorMsg.c_str()), fd)) {
+            ALOGW("Failed to write error message to fd");
+            return false;
+        }
+    }
+
+    return WriteStringToFd(StringPrintf(kHelpText, kHelpFlag, kHelpShortFlag), fd) &&
+            mIoPerfCollection->dumpHelpText(fd);
+}
+
 Status WatchdogBinderMediator::registerMediator(const sp<ICarWatchdogClient>& mediator) {
     Status status = checkSystemPermission();
     if (!status.isOk()) {
diff --git a/watchdog/server/src/WatchdogBinderMediator.h b/watchdog/server/src/WatchdogBinderMediator.h
index a3b19a2..530659b 100644
--- a/watchdog/server/src/WatchdogBinderMediator.h
+++ b/watchdog/server/src/WatchdogBinderMediator.h
@@ -83,6 +83,8 @@
     void binderDied(const android::wp<IBinder>& who) override {
         return mWatchdogProcessService->binderDied(who);
     }
+    bool dumpHelpText(int fd, std::string errorMsg);
+
     android::sp<WatchdogProcessService> mWatchdogProcessService;
     android::sp<IoPerfCollection> mIoPerfCollection;