[automerged blank] Import translations. DO NOT MERGE ANYWHERE 2p: f1249b97bd

Blank merge reason: commit message contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Car/+/15995005

Change-Id: Ia0738af6b59f189fdf7d4099f8b6e0a3a942c2b1
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1b105cc..926a882 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
           "include-filter": "android.car.apitest.PreInstalledPackagesTest"
         }
       ]
+    },
+    {
+      "name": "SampleCustomInputServiceTest"
     }
   ]
 }
\ No newline at end of file
diff --git a/car-lib/src/android/car/content/pm/CarPackageManager.java b/car-lib/src/android/car/content/pm/CarPackageManager.java
index e66d851..27eff37 100644
--- a/car-lib/src/android/car/content/pm/CarPackageManager.java
+++ b/car-lib/src/android/car/content/pm/CarPackageManager.java
@@ -16,20 +16,28 @@
 
 package android.car.content.pm;
 
+import static android.car.Car.PERMISSION_CONTROL_APP_BLOCKING;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.car.Car;
 import android.car.CarManagerBase;
 import android.content.ComponentName;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Provides car specific API related with package management.
@@ -77,6 +85,36 @@
     @Deprecated
     public static final int FLAG_SET_POLICY_REMOVE = 0x4;
 
+    /**
+     * Represents support of all regions for driving safety.
+     *
+     * @hide
+     */
+    public static final String DRIVING_SAFETY_REGION_ALL = "android.car.drivingsafetyregion.all";
+
+    /**
+     * Metadata which Activity can use to specify the driving safety regions it is supporting.
+     *
+     * <p>Definition of driving safety region is car OEM specific for now and only OEM apps
+     * should use this. If there are multiple regions, it should be comma separated. Not specifying
+     * this means supporting all regions.
+     *
+     * <p>Some examples are:
+     *   <meta-data android:name="android.car.drivingsafetyregions"
+     *   android:value="com.android.drivingsafetyregion.1,com.android.drivingsafetyregion.2"/>
+     *
+     * @hide
+     */
+    public static final String DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS =
+            "android.car.drivingsafetyregions";
+
+    /**
+     * Internal error code for throwing {@code NameNotFoundException} from service.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NO_PACKAGE = -100;
+
     /** @hide */
     @IntDef(flag = true,
             value = {FLAG_SET_POLICY_WAIT_FOR_CHANGE, FLAG_SET_POLICY_ADD, FLAG_SET_POLICY_REMOVE})
@@ -242,4 +280,108 @@
             return handleRemoteExceptionFromCarService(e, false);
         }
     }
+
+    /**
+     * Returns the current driving safety region of the system. It will return OEM specific regions
+     * or {@link #DRIVING_SAFETY_REGION_ALL} when all regions are supported.
+     *
+     * <p> System's driving safety region is static and does not change until system restarts.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {PERMISSION_CONTROL_APP_BLOCKING,
+            Car.PERMISSION_CAR_DRIVING_STATE})
+    @NonNull
+    public String getCurrentDrivingSafetyRegion() {
+        try {
+            return mService.getCurrentDrivingSafetyRegion();
+        } catch (RemoteException e) {
+            return handleRemoteExceptionFromCarService(e, DRIVING_SAFETY_REGION_ALL);
+        }
+    }
+
+    /**
+     * Enables or disables bypassing of unsafe {@code Activity} blocking for a specific
+     * {@code Activity} temporarily.
+     *
+     * <p> Enabling bypassing only lasts until the user stops using the car or until a user
+     * switching happens. Apps like launcher may ask user's consent to bypass. Note that bypassing
+     * is done for the package for all android users including the current user and user 0.
+     * <p> If bypassing is disabled and if the unsafe app is in foreground with driving state, the
+     * app will be immediately blocked.
+     *
+     * @param packageName Target package name.
+     * @param activityClassName Target Activity name (in full class name).
+     * @param bypass Bypass {@code Activity} blocking when true. Do not bypass anymore when false.
+     * @param userId User Id where the package is installed. Even if the bypassing is enabled for
+     *               all android users, the package should be available for the specified user id.
+     *
+     * @throws NameNotFoundException If the given package / Activity class does not exist for the
+     *         user.
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {PERMISSION_CONTROL_APP_BLOCKING,
+            android.Manifest.permission.QUERY_ALL_PACKAGES})
+    public void controlTemporaryActivityBlockingBypassingAsUser(String packageName,
+            String activityClassName, boolean bypass, @UserIdInt int userId)
+            throws NameNotFoundException {
+        try {
+            mService.controlOneTimeActivityBlockingBypassingAsUser(packageName, activityClassName,
+                    bypass, userId);
+        } catch (ServiceSpecificException e) {
+            handleServiceSpecificFromCarService(e, packageName, activityClassName, userId);
+        } catch (RemoteException e) {
+            handleRemoteExceptionFromCarService(e);
+        }
+    }
+
+    /**
+     * Returns all supported driving safety regions for the given Activity. If the Activity supports
+     * all regions, it will only include {@link #DRIVING_SAFETY_REGION_ALL}.
+     *
+     * <p> The permission specification requires {@code PERMISSION_CONTROL_APP_BLOCKING} and
+     * {@code QUERY_ALL_PACKAGES} but this API will also work if the client has
+     * {@link Car#PERMISSION_CAR_DRIVING_STATE} and {@code QUERY_ALL_PACKAGES} permissions.
+     *
+     * @param packageName Target package name.
+     * @param activityClassName Target Activity name (in full class name).
+     * @param userId Android user Id to check the package.
+     *
+     * @return Empty list if the Activity does not support driving safety (=no
+     *         {@code distractionOptimized} metadata). Otherwise returns full list of all supported
+     *         regions.
+     *
+     * @throws NameNotFoundException If the given package / Activity class does not exist for the
+     *         user.
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {PERMISSION_CONTROL_APP_BLOCKING,
+            android.Manifest.permission.QUERY_ALL_PACKAGES})
+    @NonNull
+    public List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName,
+            String activityClassName, @UserIdInt int userId) throws NameNotFoundException {
+        try {
+            return mService.getSupportedDrivingSafetyRegionsForActivityAsUser(packageName,
+                    activityClassName, userId);
+        } catch (ServiceSpecificException e) {
+            handleServiceSpecificFromCarService(e, packageName, activityClassName, userId);
+        } catch (RemoteException e) {
+            return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST);
+        }
+        return Collections.EMPTY_LIST; // cannot reach here but the compiler complains.
+    }
+
+    private void handleServiceSpecificFromCarService(ServiceSpecificException e,
+            String packageName, String activityClassName, @UserIdInt int userId)
+            throws NameNotFoundException {
+        if (e.errorCode == ERROR_CODE_NO_PACKAGE) {
+            throw new NameNotFoundException(
+                    "cannot find " + packageName + "/" + activityClassName + " for user id:"
+                            + userId);
+        }
+        // don't know what this is
+        throw new IllegalStateException(e);
+    }
 }
diff --git a/car-lib/src/android/car/content/pm/ICarPackageManager.aidl b/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
index b8261f2..b0bdc7c 100644
--- a/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
+++ b/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
@@ -29,4 +29,9 @@
     void setEnableActivityBlocking(boolean enable) = 4;
     void restartTask(int taskId) = 5;
     boolean isPendingIntentDistractionOptimized(in PendingIntent pendingIntent) = 6;
+    String getCurrentDrivingSafetyRegion() = 7;
+    void controlOneTimeActivityBlockingBypassingAsUser(String packageName, String activityClassName,
+            boolean bypass, int userId) = 8;
+    List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName,
+            String activityClassName, int userId) = 9;
 }
diff --git a/cpp/libsysfsmonitor/Android.bp b/cpp/libsysfsmonitor/Android.bp
new file mode 100644
index 0000000..545255b
--- /dev/null
+++ b/cpp/libsysfsmonitor/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2021 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "libsysfsmonitor_defaults",
+    cflags: [
+        "-Wall",
+        "-Wno-missing-field-initializers",
+        "-Werror",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libsysfsmonitor",
+    srcs: [
+        "src/SysfsMonitor.cpp",
+    ],
+    defaults: [
+        "libsysfsmonitor_defaults",
+    ],
+    export_include_dirs: [
+        "src",
+    ],
+}
+
+cc_test {
+    name: "libsysfsmonitor_test",
+    srcs: [
+        "tests/SysfsMonitorTest.cpp",
+    ],
+    defaults: [
+        "libsysfsmonitor_defaults",
+    ],
+    static_libs: [
+        "libgtest",
+        "libsysfsmonitor",
+    ],
+}
diff --git a/cpp/libsysfsmonitor/src/SysfsMonitor.cpp b/cpp/libsysfsmonitor/src/SysfsMonitor.cpp
new file mode 100644
index 0000000..ee89ec0
--- /dev/null
+++ b/cpp/libsysfsmonitor/src/SysfsMonitor.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2021, 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.
+ */
+
+#define LOG_TAG "libsysfsmonitor"
+#define DEBUG false
+
+#include "SysfsMonitor.h"
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+
+#include <sys/epoll.h>
+
+namespace {
+
+using ::android::base::Error;
+using ::android::base::Result;
+using ::android::base::StringPrintf;
+
+// The maximum number of sysfs files to monitor.
+constexpr int32_t EPOLL_MAX_EVENTS = 10;
+
+}  // namespace
+
+namespace android {
+namespace automotive {
+
+Result<void> SysfsMonitor::init(CallbackFunc callback) {
+    if (mEpollFd >= 0) {
+        return Error() << "Epoll instance was already created";
+    }
+    if (mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC)); mEpollFd < 0) {
+        return Error() << "Cannot create epoll instance: errno = " << errno;
+    }
+    mCallback = callback;
+    return {};
+}
+
+Result<void> SysfsMonitor::release() {
+    if (mEpollFd < 0) {
+        return Error() << "Epoll instance wasn't created";
+    }
+    for (const int32_t fd : mMonitoringFds) {
+        if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, /*event=*/nullptr)) {
+            ALOGW("Failed to deregister fd(%d) from epoll instance: errno = %d", fd, errno);
+        }
+    }
+    mMonitoringFds.clear();
+    mEpollFd.reset();
+    mCallback = nullptr;
+    return {};
+}
+
+Result<void> SysfsMonitor::registerFd(int32_t fd) {
+    if (fd < 0) {
+        return Error() << StringPrintf("fd(%d) is invalid", fd);
+    }
+    if (mMonitoringFds.count(fd) > 0) {
+        return Error() << StringPrintf("fd(%d) is already being monitored", fd);
+    }
+    if (mMonitoringFds.size() == EPOLL_MAX_EVENTS) {
+        return Error() << "Cannot monitor more than " << EPOLL_MAX_EVENTS << " sysfs files";
+    }
+    struct epoll_event eventItem = {};
+    eventItem.events = EPOLLIN | EPOLLPRI | EPOLLET;
+    eventItem.data.fd = fd;
+    if (int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem); result != 0) {
+        return Error() << StringPrintf("Failed to add fd(%d) to epoll instance: errno = %d", fd,
+                                       errno);
+    }
+    mMonitoringFds.insert(fd);
+    return {};
+}
+
+Result<void> SysfsMonitor::unregisterFd(int32_t fd) {
+    if (fd < 0) {
+        return Error() << StringPrintf("fd(%d) is invalid", fd);
+    }
+    if (mMonitoringFds.count(fd) == 0) {
+        return Error() << StringPrintf("fd(%d) is not being monitored", fd);
+    }
+    // Even when epoll_ctl() fails, we proceed to handle the request.
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, /*event=*/nullptr)) {
+        ALOGW("Failed to deregister fd(%d) from epoll instance: errno = %d", fd, errno);
+    }
+    mMonitoringFds.erase(fd);
+    return {};
+}
+
+Result<void> SysfsMonitor::observe() {
+    if (mEpollFd < 0) {
+        return Error() << "Epoll instance is not initialized";
+    }
+
+    struct epoll_event events[EPOLL_MAX_EVENTS];
+    while (true) {
+        int pollResult = epoll_wait(mEpollFd, events, EPOLL_MAX_EVENTS, /*timeout=*/-1);
+        if (pollResult < 0) {
+            ALOGW("Polling sysfs failed, but continue polling: errno = %d", errno);
+            continue;
+        }
+        std::vector<int32_t> fds;
+        for (int i = 0; i < pollResult; i++) {
+            int fd = events[i].data.fd;
+            if (mMonitoringFds.count(fd) == 0) {
+                continue;
+            }
+            if (events[i].events & EPOLLIN) {
+                fds.push_back(fd);
+            } else if (events[i].events & EPOLLERR) {
+                ALOGW("An error occurred when polling fd(%d)", fd);
+            }
+        }
+        if (mCallback && fds.size() > 0) {
+            mCallback(fds);
+        }
+    }
+    return {};
+}
+
+}  // namespace automotive
+}  // namespace android
diff --git a/cpp/libsysfsmonitor/src/SysfsMonitor.h b/cpp/libsysfsmonitor/src/SysfsMonitor.h
new file mode 100644
index 0000000..f941041
--- /dev/null
+++ b/cpp/libsysfsmonitor/src/SysfsMonitor.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2021, 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.
+ */
+
+#ifndef CPP_LIBSYSFSMONITOR_SRC_SYSFSMONITOR_H_
+#define CPP_LIBSYSFSMONITOR_SRC_SYSFSMONITOR_H_
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <utils/RefBase.h>
+
+#include <functional>
+#include <string>
+#include <unordered_set>
+
+namespace android {
+namespace automotive {
+
+using CallbackFunc = ::std::function<void(const std::vector<int32_t>&)>;
+
+/**
+ * SysfsMonitor monitors sysfs file changes and invokes the registered callback when there is a
+ * change at the sysfs files to monitor.
+ */
+class SysfsMonitor final : public RefBase {
+public:
+    // Initializes SysfsMonitor instance.
+    android::base::Result<void> init(CallbackFunc callback);
+    // Releases resources used for monitoring.
+    android::base::Result<void> release();
+    // Registers a sysfs file to monitor.
+    android::base::Result<void> registerFd(int32_t fd);
+    // Unregisters a sysfs file to monitor.
+    android::base::Result<void> unregisterFd(int32_t fd);
+    // Starts observing sysfs file changes.
+    android::base::Result<void> observe();
+
+private:
+    android::base::unique_fd mEpollFd;
+    std::unordered_set<int32_t> mMonitoringFds;
+    CallbackFunc mCallback;
+};
+
+}  // namespace automotive
+}  // namespace android
+
+#endif  // CPP_LIBSYSFSMONITOR_SRC_SYSFSMONITOR_H_
diff --git a/cpp/libsysfsmonitor/tests/SysfsMonitorTest.cpp b/cpp/libsysfsmonitor/tests/SysfsMonitorTest.cpp
new file mode 100644
index 0000000..f6dda6c
--- /dev/null
+++ b/cpp/libsysfsmonitor/tests/SysfsMonitorTest.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+#include "SysfsMonitor.h"
+
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+#include <sys/socket.h>
+
+#include <vector>
+
+namespace android {
+namespace automotive {
+
+class SysfsMonitorTest : public ::testing::Test {
+public:
+    void SetUp() override { mSysfsMonitor = sp<SysfsMonitor>::make(); }
+    void TearDown() override { mSysfsMonitor = nullptr; }
+
+protected:
+    sp<SysfsMonitor> mSysfsMonitor;
+};
+
+TEST_F(SysfsMonitorTest, TestDuplicateInitialize) {
+    auto ret = mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+    ASSERT_TRUE(ret.ok()) << "First initialization should be successful: " << ret.error().message();
+    ASSERT_FALSE(mSysfsMonitor->init([](const std::vector<int32_t>&) {}).ok())
+            << "Initialization cannot be done twice";
+}
+
+TEST_F(SysfsMonitorTest, TestDuplicateRelease) {
+    auto ret = mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+    ASSERT_TRUE(ret.ok()) << "First initialization should be successful: " << ret.error().message();
+    ret = mSysfsMonitor->release();
+    ASSERT_TRUE(ret.ok()) << "Releasing the initialized instance should be successful: "
+                          << ret.error().message();
+    ASSERT_FALSE(mSysfsMonitor->release().ok()) << "Released instance cannot be released";
+}
+
+TEST_F(SysfsMonitorTest, TestNoInitRelease) {
+    ASSERT_FALSE(mSysfsMonitor->release().ok()) << "Uninitailized instance cannot be released";
+}
+
+TEST_F(SysfsMonitorTest, TestUnregisterNotRegisteredFd) {
+    // A regular file cannot be registered to epoll instance. So, the test is using a socket file.
+    int32_t fd = socket(AF_UNIX, SOCK_DGRAM, /*protocol=*/0);
+    mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+
+    ASSERT_FALSE(mSysfsMonitor->unregisterFd(fd).ok())
+            << "Unregistered file description cannot be unregistered";
+    close(fd);
+}
+
+TEST_F(SysfsMonitorTest, TestDuplicateRegister) {
+    int32_t fd = socket(AF_UNIX, SOCK_DGRAM, /*protocol=*/0);
+    mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+
+    auto ret = mSysfsMonitor->registerFd(fd);
+    ASSERT_TRUE(ret.ok()) << "Registering a file descriptor first time should be successful: "
+                          << ret.error().message();
+    ASSERT_FALSE(mSysfsMonitor->registerFd(fd).ok())
+            << "Registering a file descriptor twice cannot be done";
+    close(fd);
+}
+
+TEST_F(SysfsMonitorTest, TestDuplicateUnregister) {
+    int32_t fd = socket(AF_UNIX, SOCK_DGRAM, /*protocol=*/0);
+    mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+
+    auto ret = mSysfsMonitor->registerFd(fd);
+    ASSERT_TRUE(ret.ok()) << "Registering a file descriptor first time should be successful: "
+                          << ret.error().message();
+    ret = mSysfsMonitor->unregisterFd(fd);
+    ASSERT_TRUE(ret.ok()) << "The registered file descriptor should be unregistered: "
+                          << ret.error().message();
+    ASSERT_FALSE(mSysfsMonitor->unregisterFd(fd).ok())
+            << "Unregistering the unregistered file descriptor cannot be done";
+    close(fd);
+}
+
+TEST_F(SysfsMonitorTest, TestRegisterInvalidFd) {
+    const int32_t invalidFd = -1;
+    ASSERT_FALSE(mSysfsMonitor->registerFd(invalidFd).ok())
+            << "fd(-1) cannot be registered to SysfsMonitor";
+}
+
+TEST_F(SysfsMonitorTest, TestUnregisterInvalidFd) {
+    const int32_t invalidFd = -1;
+    ASSERT_FALSE(mSysfsMonitor->unregisterFd(invalidFd).ok())
+            << "fd(-1) cannot be given to unregisterFd";
+}
+
+TEST_F(SysfsMonitorTest, TestRegisterMultipleFds) {
+    const int32_t maxFdCount = 10;
+    int32_t fdsToMonitor[maxFdCount + 1];
+
+    mSysfsMonitor->init([](const std::vector<int32_t>&) {});
+    for (int i = 0; i < maxFdCount; i++) {
+        fdsToMonitor[i] = socket(AF_UNIX, SOCK_DGRAM, /*protocol=*/0);
+        auto ret = mSysfsMonitor->registerFd(fdsToMonitor[i]);
+        ASSERT_TRUE(ret.ok()) << "Registering a file descriptor first time should be successful: "
+                              << ret.error().message();
+    }
+    fdsToMonitor[maxFdCount] = socket(AF_UNIX, SOCK_DGRAM, /*protocol=*/0);
+    ASSERT_FALSE(mSysfsMonitor->registerFd(fdsToMonitor[maxFdCount]).ok())
+            << "Registering more than " << maxFdCount << " files cannot be done";
+
+    mSysfsMonitor->release();
+    for (int i = 0; i <= maxFdCount; i++) {
+        close(fdsToMonitor[i]);
+    }
+}
+
+TEST_F(SysfsMonitorTest, TestObserveWithoutInitialization) {
+    ASSERT_FALSE(mSysfsMonitor->observe().ok()) << "Uninitialized instance cannot observe";
+}
+
+}  // namespace automotive
+}  // namespace android
diff --git a/cpp/powerpolicy/server/Android.bp b/cpp/powerpolicy/server/Android.bp
index 094a3f5..2dd40bd 100644
--- a/cpp/powerpolicy/server/Android.bp
+++ b/cpp/powerpolicy/server/Android.bp
@@ -36,6 +36,9 @@
         "libtinyxml2",
         "libutils",
     ],
+    static_libs: [
+        "libsysfsmonitor",
+    ],
 }
 
 cc_library {
diff --git a/cpp/powerpolicy/server/src/SilentModeHandler.cpp b/cpp/powerpolicy/server/src/SilentModeHandler.cpp
index c57b6fa..79d78be 100644
--- a/cpp/powerpolicy/server/src/SilentModeHandler.cpp
+++ b/cpp/powerpolicy/server/src/SilentModeHandler.cpp
@@ -25,7 +25,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include <sys/inotify.h>
+#include <sys/epoll.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -35,6 +35,7 @@
 namespace powerpolicy {
 
 using ::android::Mutex;
+using ::android::automotive::SysfsMonitor;
 using ::android::base::Error;
 using ::android::base::GetProperty;
 using ::android::base::ReadFileToString;
@@ -49,12 +50,11 @@
 
 constexpr const char kPropertySystemBootReason[] = "sys.boot.reason";
 constexpr const char kSilentModeHwStateFilename[] = "/sys/power/pm_silentmode_hw_state";
-constexpr const char kKernelSilentModeFilename[] = "/sys/power/pm_silentmode_kernel";
+constexpr const char kKernelSilentModeFilename[] = "/sys/power/pm_silentmode_kernel_state";
 // To prevent boot animation from being started.
 constexpr const char kPropertyNoBootAnimation[] = "debug.sf.nobootanimation";
 // To stop boot animation while it is being played.
 constexpr const char kPropertyBootAnimationExit[] = "service.bootanim.exit";
-constexpr int kEventBufferSize = 512;
 
 bool fileExists(const char* filename) {
     struct stat buffer;
@@ -68,7 +68,7 @@
       mSilentModeHwStateFilename(kSilentModeHwStateFilename),
       mKernelSilentModeFilename(kKernelSilentModeFilename),
       mSilentModeChangeHandler(handler),
-      mFdInotify(-1) {
+      mSysfsMonitor(sp<SysfsMonitor>::make()) {
     mBootReason = GetProperty(kPropertySystemBootReason, "");
 }
 
@@ -83,7 +83,7 @@
     if (mForcedMode) {
         handleSilentModeChange(mSilentModeByHwState);
         mSilentModeChangeHandler->notifySilentModeChange(mSilentModeByHwState);
-        ALOGI("Now in forced mode: monitoring %s is disabled", kSilentModeHwStateFilename);
+        ALOGI("Now in forced mode: monitoring %s is disabled", mSilentModeHwStateFilename.c_str());
     } else {
         startMonitoringSilentModeHwState();
     }
@@ -101,13 +101,15 @@
 void SilentModeHandler::stopMonitoringSilentModeHwState(bool shouldWaitThread) {
     if (mIsMonitoring) {
         mIsMonitoring = false;
-        inotify_rm_watch(mFdInotify, mWdSilentModeHwState);
-        mWdSilentModeHwState = -1;
+        if (auto ret = mSysfsMonitor->unregisterFd(mFdSilentModeHwState.get()); !ret.ok()) {
+            ALOGW("Unregistering %s from SysfsMonitor failed", mSilentModeHwStateFilename.c_str());
+        }
         if (shouldWaitThread && mSilentModeMonitoringThread.joinable()) {
             mSilentModeMonitoringThread.join();
         }
     }
-    mFdInotify.reset(-1);
+    mFdSilentModeHwState.reset();
+    mSysfsMonitor->release();
 }
 
 Result<void> SilentModeHandler::dump(int fd, const Vector<String16>& /*args*/) {
@@ -132,56 +134,38 @@
         ALOGW("Silent Mode monitoring is already started");
         return;
     }
-    if (mFdInotify < 0) {
-        mFdInotify.reset(inotify_init1(IN_CLOEXEC));
-        if (mFdInotify < 0) {
-            ALOGE("Failed to start monitoring Silent Mode HW state: creating inotify instance "
-                  "failed (errno = %d)",
-                  errno);
-            return;
-        }
-    }
     const char* filename = mSilentModeHwStateFilename.c_str();
     if (!fileExists(filename)) {
         ALOGW("Failed to start monitoring Silent Mode HW state: %s doesn't exist", filename);
-        mFdInotify.reset(-1);
         return;
     }
-    // TODO(b/178843534): Additional masks might be needed to detect sysfs change.
-    const uint32_t masks = IN_MODIFY;
-    mWdSilentModeHwState = inotify_add_watch(mFdInotify, filename, masks);
+    if (mFdSilentModeHwState == -1) {
+        if (mFdSilentModeHwState.reset(open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+            mFdSilentModeHwState == -1) {
+            ALOGW("Failed to open %s for monitoring: errno = %d", filename, errno);
+            return;
+        }
+    }
+    auto ret = mSysfsMonitor->init([this](const std::vector<int32_t>& fileDescriptors) {
+        // Only one sysfs file is registered.
+        if (mFdSilentModeHwState != fileDescriptors[0]) {
+            return;
+        }
+        handleSilentModeHwStateChange();
+    });
+    if (!ret.ok()) {
+        ALOGW("Failed to initialize SysfsMonitor: %s", ret.error().message().c_str());
+        return;
+    }
+    if (auto ret = mSysfsMonitor->registerFd(mFdSilentModeHwState.get()); !ret.ok()) {
+        ALOGW("Failed to register %s to SysfsMonitor: %s", filename, ret.error().message().c_str());
+        return;
+    }
     mIsMonitoring = true;
-    mSilentModeMonitoringThread = std::thread([this]() {
-        char eventBuf[kEventBufferSize];
-        struct inotify_event* event;
-        constexpr size_t inotifyEventSize = sizeof(*event);
-        ALOGI("Monitoring %s started", mSilentModeHwStateFilename.c_str());
-        while (mIsMonitoring) {
-            int eventPos = 0;
-            int numBytes = read(mFdInotify, eventBuf, sizeof(eventBuf));
-            if (numBytes < static_cast<int>(inotifyEventSize)) {
-                if (errno == EINTR) {
-                    ALOGW("System call interrupted. Wait for inotify event again.");
-                    continue;
-                }
-                mIsMonitoring = false;
-                inotify_rm_watch(mFdInotify, mWdSilentModeHwState);
-                mWdSilentModeHwState = -1;
-                mFdInotify.reset(-1);
-                ALOGW("Failed to wait for change at %s (errno = %d)",
-                      mSilentModeHwStateFilename.c_str(), errno);
-                return;
-            }
-            while (numBytes >= static_cast<int>(inotifyEventSize)) {
-                int eventSize;
-                event = (struct inotify_event*)(eventBuf + eventPos);
-                if (event->wd == mWdSilentModeHwState && (event->mask & masks)) {
-                    handleSilentModeHwStateChange();
-                }
-                eventSize = inotifyEventSize + event->len;
-                numBytes -= eventSize;
-                eventPos += eventSize;
-            }
+    mSilentModeMonitoringThread = std::thread([this, filename]() {
+        if (auto ret = mSysfsMonitor->observe(); !ret.ok()) {
+            ALOGI("Failed to observe %s", filename);
+            return;
         }
         ALOGI("Monitoring %s ended", mSilentModeHwStateFilename.c_str());
     });
diff --git a/cpp/powerpolicy/server/src/SilentModeHandler.h b/cpp/powerpolicy/server/src/SilentModeHandler.h
index 9fae109..96074f8 100644
--- a/cpp/powerpolicy/server/src/SilentModeHandler.h
+++ b/cpp/powerpolicy/server/src/SilentModeHandler.h
@@ -21,8 +21,11 @@
 #include <android-base/unique_fd.h>
 #include <utils/Mutex.h>
 #include <utils/String16.h>
+#include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
+#include <SysfsMonitor.h>
+
 #include <atomic>
 #include <thread>  // NOLINT(build/c++11)
 
@@ -47,8 +50,8 @@
 
 /**
  * SilentModeHandler monitors {@code /sys/power/pm_silentmode_hw_state} in sysfs to detect Silent
- * Mode change by a vehicle processor. Also, it updates {@code /sys/power/pm_silentmode_kernel} in
- * sysfs to tell kernel the current Silent Mode.
+ * Mode change by a vehicle processor. Also, it updates
+ * {@code /sys/power/pm_silentmode_kernel_state} in sysfs to tell kernel the current Silent Mode.
  */
 class SilentModeHandler final {
 public:
@@ -80,9 +83,9 @@
     std::string mKernelSilentModeFilename;
     ISilentModeChangeHandler* mSilentModeChangeHandler;
     std::thread mSilentModeMonitoringThread;
-    android::base::unique_fd mFdInotify;
-    int mWdSilentModeHwState = -1;
     std::atomic_bool mIsMonitoring = false;
+    android::sp<android::automotive::SysfsMonitor> mSysfsMonitor;
+    android::base::unique_fd mFdSilentModeHwState;
 
     // For unit tests.
     friend class internal::SilentModeHandlerPeer;
diff --git a/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp b/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
index 1eaec81..6548cf1 100644
--- a/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
+++ b/cpp/powerpolicy/server/tests/SilentModeHandlerTest.cpp
@@ -46,20 +46,6 @@
 constexpr int kMaxPollingAttempts = 5;
 constexpr std::chrono::microseconds kPollingDelayUs = 50ms;
 
-bool waitForSilentMode(SilentModeHandler* handler, bool expectedSilentMode) {
-    int count = 0;
-    while (true) {
-        if (handler->isSilentMode() == expectedSilentMode) {
-            return true;
-        }
-        if (count++; count == kMaxPollingAttempts) {
-            break;
-        }
-        usleep(kPollingDelayUs.count());
-    }
-    return false;
-}
-
 }  // namespace
 
 namespace internal {
@@ -124,23 +110,6 @@
     sp<MockCarPowerPolicyServer> carPowerPolicyServer;
 };
 
-TEST_F(SilentModeHandlerTest, TestSilentModeHwStateMonitoring) {
-    SilentModeHandler handler(carPowerPolicyServer.get());
-    internal::SilentModeHandlerPeer handlerPeer(&handler);
-    handlerPeer.injectBootReason(kBootReasonNormal);
-    handlerPeer.init();
-
-    handlerPeer.updateSilentModeHwState(/*isSilent=*/true);
-
-    ASSERT_TRUE(waitForSilentMode(&handler, /*expectedSilentMode=*/true))
-            << "It should be silent mode when HW state is on";
-
-    handlerPeer.updateSilentModeHwState(/*isSilent=*/false);
-
-    ASSERT_TRUE(waitForSilentMode(&handler, /*expectedSilentMode=*/false))
-            << "It should be non-silent mode when HW state is off";
-}
-
 TEST_F(SilentModeHandlerTest, TestRebootForForcedSilentMode) {
     SilentModeHandler handler(carPowerPolicyServer.get());
     internal::SilentModeHandlerPeer handlerPeer(&handler);
diff --git a/service/res/values-te/strings.xml b/service/res/values-te/strings.xml
index 9993cd8..87e6dc8 100644
--- a/service/res/values-te/strings.xml
+++ b/service/res/values-te/strings.xml
@@ -19,7 +19,7 @@
     <string name="car_permission_label" msgid="2215078736675564541">"కారు సమాచారం"</string>
     <string name="car_permission_desc" msgid="3584369074931334964">"మీ కారుకు సంబంధించిన సమాచారాన్ని యాక్సెస్ చేయండి"</string>
     <string name="car_permission_label_camera" msgid="3725702064841827180">"కారు కెమెరాను యాక్సెస్ చేయగలవు"</string>
-    <string name="car_permission_desc_camera" msgid="917024932164501426">"మీ కారు కామెరా(ల)ను యాక్సెస్ చేయడం."</string>
+    <string name="car_permission_desc_camera" msgid="917024932164501426">"మీ కారు యొక్క కామెరా(లు)ని యాక్సెస్ చేయండి."</string>
     <string name="car_permission_label_energy" msgid="7409144323527821558">"కారు శక్తి సమాచారాన్ని యాక్సెస్ చేయగలవు"</string>
     <string name="car_permission_desc_energy" msgid="3392963810053235407">"మీ కారు శక్తి సమాచారాన్ని యాక్సెస్ చేయండి."</string>
     <string name="car_permission_label_adjust_range_remaining" msgid="839033553999920138">"కారు యొక్క మిగిలిన ప్రయాణ దూరాన్ని సర్దుబాటు చేయండి"</string>
@@ -84,9 +84,9 @@
     <string name="car_permission_label_diag_clear" msgid="4783070510879698157">"సమస్య విశ్లేషణ డేటాను క్లియర్ చేయగలవు"</string>
     <string name="car_permission_desc_diag_clear" msgid="7453222114866042786">"కారు నుండి సమస్య విశ్లేషణ డేటాను క్లియర్ చేయగలవు."</string>
     <string name="car_permission_label_vms_publisher" msgid="3049934078926106641">"VMS ప్రచురణకర్త"</string>
-    <string name="car_permission_desc_vms_publisher" msgid="5589489298597386828">"VMS మెసేజ్‌లను పబ్లిష్ చేయండి"</string>
+    <string name="car_permission_desc_vms_publisher" msgid="5589489298597386828">"VMS మెసేజ్‌లను ప్రచురించండి"</string>
     <string name="car_permission_label_vms_subscriber" msgid="5648841182059222299">"VMS సభ్యులు"</string>
-    <string name="car_permission_desc_vms_subscriber" msgid="7551009457847673620">"VMS మెసేజ్‌లను పొందడానికి సబ్‌స్క్రయిబ్ చేయండి"</string>
+    <string name="car_permission_desc_vms_subscriber" msgid="7551009457847673620">"VMS మెసేజ్‌లను పొందడానికి సభ్యత్వం తీసుకోండి"</string>
     <string name="car_permission_label_bind_vms_client" msgid="4889732900973280313">"VMS క్లయింట్ సేవ"</string>
     <string name="car_permission_desc_bind_vms_client" msgid="4062835325264330564">"VMS క్లయింట్‌లను ఆచరించండి"</string>
     <string name="car_permission_label_storage_monitoring" msgid="2327639346522530549">"ఫ్లాష్ నిల్వ పర్యవేక్షణ"</string>
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index bad6b71..083eeb8 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -40,6 +40,7 @@
 import android.car.Car;
 import android.car.CarOccupantZoneManager;
 import android.car.VehiclePropertyIds;
+import android.car.content.pm.CarPackageManager;
 import android.car.input.CarInputManager;
 import android.car.input.CustomInputEvent;
 import android.car.input.RotaryEvent;
@@ -198,6 +199,9 @@
     private static final String COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES =
             "watchdog-io-get-3p-foreground-bytes";
 
+    private static final String COMMAND_DRIVING_SAFETY_SET_REGION =
+            "set-drivingsafety-region";
+
     private static final String[] CREATE_OR_MANAGE_USERS_PERMISSIONS = new String[] {
             android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.MANAGE_USERS
@@ -612,6 +616,10 @@
 
         pw.printf("\t%s\n", COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES);
         pw.println("\t  Gets third-party apps foreground I/O overuse threshold");
+
+        pw.printf("\t%s [REGION_STRING]", COMMAND_DRIVING_SAFETY_SET_REGION);
+        pw.println("\t  Set driving safety region.");
+        pw.println("\t  Skipping REGION_STRING leads into resetting to all regions");
     }
 
     private static int showInvalidArguments(IndentingPrintWriter pw) {
@@ -940,7 +948,9 @@
             case COMMAND_WATCHDOG_IO_GET_3P_FOREGROUND_BYTES:
                 getWatchdogIoThirdPartyForegroundBytes(writer);
                 break;
-
+            case COMMAND_DRIVING_SAFETY_SET_REGION:
+                setDrivingSafetyRegion(args, writer);
+                break;
             default:
                 writer.println("Unknown command: \"" + cmd + "\"");
                 showHelp(writer);
@@ -2101,6 +2111,17 @@
         }
     }
 
+    private void setDrivingSafetyRegion(String[] args, IndentingPrintWriter writer) {
+        if (args.length != 1 && args.length != 2) {
+            showInvalidArguments(writer);
+            return;
+        }
+        String region = args.length == 2 ? args[1] : CarPackageManager.DRIVING_SAFETY_REGION_ALL;
+        writer.println("Set driving safety region to:" + region);
+        CarLocalServices.getService(CarPackageManagerService.class).resetDrivingSafetyRegion(
+                region);
+    }
+
     private void getRearviewCameraId(IndentingPrintWriter writer) {
         writer.printf("CarEvsService is using %s for the rearview.\n",
                 mCarEvsService.getRearviewCameraIdFromCommand());
diff --git a/service/src/com/android/car/pm/CarAppMetadataReader.java b/service/src/com/android/car/pm/CarAppMetadataReader.java
index 2b67f25..0e7c136 100644
--- a/service/src/com/android/car/pm/CarAppMetadataReader.java
+++ b/service/src/com/android/car/pm/CarAppMetadataReader.java
@@ -15,19 +15,25 @@
  */
 package com.android.car.pm;
 
+import static android.car.content.pm.CarPackageManager.DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.car.content.pm.CarPackageManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.car.CarLog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -49,19 +55,13 @@
     /** Name of the meta-data attribute of the Activity that denotes distraction optimized */
     private static final String DO_METADATA_ATTRIBUTE = "distractionOptimized";
 
-    /**
-     * Parses the given package and returns Distraction Optimized information, if present.
-     *
-     * @return Array of DO activity names in the given package
-     */
-    @Nullable
-    public static String[] findDistractionOptimizedActivitiesAsUser(Context context,
-            String packageName, int userId) throws NameNotFoundException {
-        final PackageManager pm = context.getPackageManager();
+    private static final List<String> ALL_REGION_ONLY = Collections.singletonList(
+            CarPackageManager.DRIVING_SAFETY_REGION_ALL);
 
-        // Check if any of the activities in the package are DO by checking all the
-        // <activity> elements. MATCH_DISABLED_COMPONENTS is included so that we are immediately
-        // prepared to respond to any components that toggle from disabled to enabled.
+    @Nullable
+    private static ActivityInfo[] getAllActivitiesForPackageAsUser(Context context,
+            String packageName, @UserIdInt int userId)  throws NameNotFoundException {
+        final PackageManager pm = context.getPackageManager();
         PackageInfo pkgInfo =
                 pm.getPackageInfoAsUser(
                         packageName, PackageManager.GET_ACTIVITIES
@@ -74,18 +74,43 @@
             return null;
         }
 
-        ActivityInfo[] activities = pkgInfo.activities;
+        return pkgInfo.activities;
+    }
+
+    /**
+     * Parses the given package and returns Distraction Optimized information, if present.
+     *
+     * @return Array of DO activity names in the given package
+     */
+    @Nullable
+    public static String[] findDistractionOptimizedActivitiesAsUser(Context context,
+            String packageName, @UserIdInt int userId, @NonNull String drivingSafetyRegion)
+            throws NameNotFoundException {
+
+
+        // Check if any of the activities in the package are DO by checking all the
+        // <activity> elements. MATCH_DISABLED_COMPONENTS is included so that we are immediately
+        // prepared to respond to any components that toggle from disabled to enabled.
+        ActivityInfo[] activities = getAllActivitiesForPackageAsUser(context, packageName, userId);
         if (activities == null) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (CarPackageManagerService.DBG) {
                 Slog.d(TAG, "Null Activities for " + packageName);
             }
             return null;
         }
         List<String> optimizedActivityList = new ArrayList();
         for (ActivityInfo activity : activities) {
-            Bundle mData = activity.metaData;
-            if (mData != null && mData.getBoolean(DO_METADATA_ATTRIBUTE, false)) {
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Bundle metaData = activity.metaData;
+            if (metaData == null) {
+                continue;
+            }
+            String regionString = metaData.getString(DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS,
+                    CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+            if (!isRegionSupported(regionString, drivingSafetyRegion)) {
+                continue;
+            }
+            if (metaData.getBoolean(DO_METADATA_ATTRIBUTE, false)) {
+                if (CarPackageManagerService.DBG) {
                     Slog.d(TAG,
                             "DO Activity:" + activity.packageName + "/" + activity.name);
                 }
@@ -97,4 +122,66 @@
         }
         return optimizedActivityList.toArray(new String[optimizedActivityList.size()]);
     }
+
+    /**
+     * Check {@link CarPackageManager#getSupportedDrivingSafetyRegionsForActivityAsUser(
+     * String, String, int)}.
+     */
+    public static List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(Context context,
+            String packageName, String activityClassName, @UserIdInt int userId)
+            throws NameNotFoundException {
+        ActivityInfo[] activities = getAllActivitiesForPackageAsUser(context, packageName, userId);
+        if (activities == null) {
+            throw new NameNotFoundException();
+        }
+        for (ActivityInfo info: activities) {
+            if (!info.name.equals(activityClassName)) {
+                continue;
+            }
+            // Found activity
+            Bundle metaData = info.metaData;
+            if (metaData == null) {
+                return Collections.EMPTY_LIST;
+            }
+            if (!metaData.getBoolean(DO_METADATA_ATTRIBUTE, false)) {
+                // no distractionOptimized, so region metadata does not matter.
+                return Collections.EMPTY_LIST;
+            }
+            String regionString = metaData.getString(DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS,
+                    CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+            String[] regions = regionString.split(",");
+            for (String region: regions) {
+                if (CarPackageManager.DRIVING_SAFETY_REGION_ALL.equals(region)) {
+                    return ALL_REGION_ONLY;
+                }
+            }
+            return Arrays.asList(regions);
+        }
+        throw new NameNotFoundException();
+    }
+
+    private static boolean isRegionSupported(String regionString, String currentRegion) {
+        if (regionString.isEmpty()) { // not specified means all regions.
+            return true;
+        }
+        if (currentRegion.equals(CarPackageManager.DRIVING_SAFETY_REGION_ALL)) {
+            return true;
+        }
+        String[] regions = regionString.split(",");
+        for (String region: regions) {
+            if (CarPackageManager.DRIVING_SAFETY_REGION_ALL.equals(region)) {
+                return true;
+            }
+            if (currentRegion.equals(region)) {
+                return true;
+            }
+        }
+        // valid regions but does not match currentRegion.
+        if (CarPackageManagerService.DBG) {
+            Slog.d(TAG,
+                    "isRegionSupported not supported, regionString:" + regionString
+                            + " region:" + currentRegion);
+        }
+        return false;
+    }
 }
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 8c8a105..4c262b5 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -32,6 +32,11 @@
 import android.car.content.pm.ICarPackageManager;
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
+import android.car.hardware.power.CarPowerPolicy;
+import android.car.hardware.power.CarPowerPolicyFilter;
+import android.car.hardware.power.ICarPowerPolicyListener;
+import android.car.hardware.power.PowerComponent;
+import android.car.user.CarUserManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -53,6 +58,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
@@ -64,6 +71,7 @@
 import android.view.Display;
 import android.view.DisplayAddress;
 
+import com.android.car.CarLocalServices;
 import com.android.car.CarLog;
 import com.android.car.CarServiceBase;
 import com.android.car.CarServiceUtils;
@@ -71,6 +79,8 @@
 import com.android.car.R;
 import com.android.car.SystemActivityMonitoringService;
 import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
+import com.android.car.power.CarPowerManagementService;
+import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -90,6 +100,8 @@
 
 public class CarPackageManagerService extends ICarPackageManager.Stub implements CarServiceBase {
 
+    static final boolean DBG = false;
+
     private static final String TAG = CarLog.tagFor(CarPackageManagerService.class);
 
     // Delimiters to parse packages and activities in the configuration XML resource.
@@ -97,6 +109,9 @@
     private static final String PACKAGE_ACTIVITY_DELIMITER = "/";
     private static final int LOG_SIZE = 20;
 
+    private static final String PROPERTY_RO_DRIVING_SAFETY_REGION =
+            "ro.android.car.drivingsafetyregion";
+
     private final Context mContext;
     private final SystemActivityMonitoringService mSystemActivityMonitoringService;
     private final PackageManager mPackageManager;
@@ -139,8 +154,15 @@
     @GuardedBy("mLock")
     private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>();
 
+    @GuardedBy("mLock")
+    private String mCurrentDrivingSafetyRegion = CarPackageManager.DRIVING_SAFETY_REGION_ALL;
+    // Package name + '/' + className format
+    @GuardedBy("mLock")
+    private final ArraySet<String> mTempAllowedActivities = new ArraySet<>();
+
     private final CarUxRestrictionsManagerService mCarUxRestrictionsService;
-    private boolean mEnableActivityBlocking;
+    private final boolean mEnableActivityBlocking;
+
     private final ComponentName mActivityBlockingActivity;
 
     private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener();
@@ -195,6 +217,32 @@
      */
     public static final String BLOCKING_INTENT_EXTRA_DISPLAY_ID = "display_id";
 
+    private final CarUserManager.UserLifecycleListener mUserLifecycleListener =
+            new CarUserManager.UserLifecycleListener() {
+                @Override
+                public void onEvent(CarUserManager.UserLifecycleEvent event) {
+                    if (event.getEventType()
+                            == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
+                        synchronized (mLock) {
+                            resetTempAllowedActivitiesLocked();
+                        }
+                    }
+                }
+            };
+
+    private final ICarPowerPolicyListener mDisplayPowerPolicyListener =
+            new ICarPowerPolicyListener.Stub() {
+                @Override
+                public void onPolicyChanged(CarPowerPolicy policy,
+                        CarPowerPolicy accumulatedPolicy) {
+                    if (!policy.isComponentEnabled(PowerComponent.DISPLAY)) {
+                        synchronized (mLock) {
+                            resetTempAllowedActivitiesLocked();
+                        }
+                    }
+                }
+            };
+
     public CarPackageManagerService(Context context,
             CarUxRestrictionsManagerService uxRestrictionsService,
             SystemActivityMonitoringService systemActivityMonitoringService) {
@@ -236,13 +284,92 @@
         mSystemActivityMonitoringService.restartTask(taskId);
     }
 
-    private void doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy,
-            int flags) {
+    @Override
+    public String getCurrentDrivingSafetyRegion() {
+        assertAppBlockingOrDrivingStatePermission();
+        synchronized (mLock) {
+            return mCurrentDrivingSafetyRegion;
+        }
+    }
+
+    private String getComponentNameString(String packageName, String className) {
+        return packageName + '/' + className;
+    }
+
+    @Override
+    public void controlOneTimeActivityBlockingBypassingAsUser(String packageName,
+            String activityClassName, boolean bypass, @UserIdInt int userId) {
+        assertAppBlockingPermission();
+        if (!callerCanQueryPackage(packageName)) {
+            throw new SecurityException("cannot query other package");
+        }
+        try {
+            // Read this to check the validity of pkg / activity name. Not checking this can allow
+            // bad apps to be allowed later.
+            CarAppMetadataReader.getSupportedDrivingSafetyRegionsForActivityAsUser(mContext,
+                    packageName, activityClassName, userId);
+        } catch (NameNotFoundException e) {
+            throw new ServiceSpecificException(CarPackageManager.ERROR_CODE_NO_PACKAGE,
+                    e.getMessage());
+        }
+        String componentName = getComponentNameString(packageName, activityClassName);
+        synchronized (mLock) {
+            if (bypass) {
+                mTempAllowedActivities.add(componentName);
+            } else {
+                mTempAllowedActivities.remove(componentName);
+            }
+        }
+        if (!bypass) { // block top activities if bypassing is disabled.
+            blockTopActivitiesIfNecessary();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void resetTempAllowedActivitiesLocked() {
+        mTempAllowedActivities.clear();
+    }
+
+    @Override
+    public List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName,
+            String activityClassName, @UserIdInt int userId) {
+        assertAppBlockingOrDrivingStatePermission();
+        if (!callerCanQueryPackage(packageName)) {
+            throw new SecurityException("cannot query other package");
+        }
+        long token = Binder.clearCallingIdentity();
+        try {
+            return CarAppMetadataReader.getSupportedDrivingSafetyRegionsForActivityAsUser(mContext,
+                    packageName, activityClassName, userId);
+        } catch (NameNotFoundException e) {
+            throw new ServiceSpecificException(CarPackageManager.ERROR_CODE_NO_PACKAGE,
+                    e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void assertAppBlockingOrDrivingStatePermission() {
+        if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING)
+                != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission(
+                Car.PERMISSION_CAR_DRIVING_STATE) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING + " or "
+                            + Car.PERMISSION_CAR_DRIVING_STATE);
+        }
+    }
+
+    private void assertAppBlockingPermission() {
         if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException(
                     "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING);
         }
+    }
+
+    private void doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy,
+            int flags) {
+        assertAppBlockingPermission();
         CarServiceUtils.assertPackageName(mContext, packageName);
         if (policy == null) {
             throw new IllegalArgumentException("policy cannot be null");
@@ -282,6 +409,11 @@
                 Slog.d(TAG, "isActivityDistractionOptimized" + dumpPoliciesLocked(false));
             }
 
+            if (mTempAllowedActivities.contains(getComponentNameString(packageName,
+                    className))) {
+                return true;
+            }
+
             if (searchFromClientPolicyBlocklistsLocked(packageName)) {
                 return false;
             }
@@ -464,13 +596,24 @@
 
     @Override
     public void init() {
+        String safetyRegion = SystemProperties.get(PROPERTY_RO_DRIVING_SAFETY_REGION, "");
         synchronized (mLock) {
+            setDrivingSafetyRegionWithCheckLocked(safetyRegion);
             mHandler.requestInit();
         }
+        CarLocalServices.getService(CarUserService.class).addUserLifecycleListener(
+                mUserLifecycleListener);
+        CarLocalServices.getService(CarPowerManagementService.class).addPowerPolicyListener(
+                new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(),
+                mDisplayPowerPolicyListener);
     }
 
     @Override
     public void release() {
+        CarLocalServices.getService(CarPowerManagementService.class).removePowerPolicyListener(
+                mDisplayPowerPolicyListener);
+        CarLocalServices.getService(CarUserService.class).removeUserLifecycleListener(
+                mUserLifecycleListener);
         synchronized (mLock) {
             mHandler.requestRelease();
             // wait for release do be done. This guarantees that init is done.
@@ -490,6 +633,7 @@
                 mProxies.clear();
             }
             mWaitingPolicies.clear();
+            resetTempAllowedActivitiesLocked();
             mLock.notifyAll();
         }
         mContext.unregisterReceiver(mPackageParsingEventReceiver);
@@ -500,6 +644,28 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void setDrivingSafetyRegionWithCheckLocked(String region) {
+        if (region.isEmpty()) {
+            mCurrentDrivingSafetyRegion = CarPackageManager.DRIVING_SAFETY_REGION_ALL;
+        } else {
+            mCurrentDrivingSafetyRegion = region;
+        }
+    }
+
+    /**
+     * Reset driving stat and all dynamically added allow list so that region information for
+     * all packages are reset. This also resets one time allow list.
+     */
+    public void resetDrivingSafetyRegion(@NonNull String region) {
+        synchronized (mLock) {
+            setDrivingSafetyRegionWithCheckLocked(region);
+            resetTempAllowedActivitiesLocked();
+            mActivityAllowlistMap.clear();
+            mActivityDenylistPackages.clear();
+        }
+    }
+
     // run from HandlerThread
     private void doHandleInit() {
         startAppBlockingPolicies();
@@ -742,9 +908,8 @@
         }
 
         try {
-            String[] doActivities =
-                    CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(
-                            mContext, info.packageName, userId);
+            String[] doActivities = findDistractionOptimizedActivitiesAsUser(info.packageName,
+                    userId);
             if (doActivities != null) {
                 // Some of the activities in this app are Distraction Optimized.
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -813,10 +978,14 @@
 
         synchronized (mLock) {
             if (wrapper != null) {
-                Slog.d(TAG, "Package: " + packageName + " added in allowlist.");
+                if (DBG) {
+                    Slog.d(TAG, "Package: " + packageName + " added in allowlist.");
+                }
                 mActivityAllowlistMap.put(packageName, wrapper);
             } else {
-                Slog.d(TAG, "Package: " + packageName + " added in denylist.");
+                if (DBG) {
+                    Slog.d(TAG, "Package: " + packageName + " added in denylist.");
+                }
                 mActivityDenylistPackages.add(packageName);
             }
         }
@@ -1008,6 +1177,10 @@
             writer.println(" Blocked activity log:");
             mBlockedActivityLogs.dump(writer);
             writer.print(dumpPoliciesLocked(true));
+            writer.print("mCurrentDrivingSafetyRegion:");
+            writer.println(mCurrentDrivingSafetyRegion);
+            writer.print("mTempAllowedActivities:");
+            writer.println(mTempAllowedActivities);
         }
     }
 
@@ -1121,13 +1294,11 @@
         if (allowed) {
             return;
         }
-        synchronized (mLock) {
-            if (!mEnableActivityBlocking) {
-                Slog.d(TAG, "Current activity " + topTask.topActivity
-                        + " not allowed, blocking disabled. Number of tasks in stack:"
-                        + topTask.taskInfo.childTaskIds.length);
-                return;
-            }
+        if (!mEnableActivityBlocking) {
+            Slog.d(TAG, "Current activity " + topTask.topActivity
+                    + " not allowed, blocking disabled. Number of tasks in stack:"
+                    + topTask.taskInfo.childTaskIds.length);
+            return;
         }
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Slog.d(TAG, "Current activity " + topTask.topActivity
@@ -1228,13 +1399,23 @@
     @Nullable
     public String[] getDistractionOptimizedActivities(String pkgName) {
         try {
-            return CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(mContext, pkgName,
+            return findDistractionOptimizedActivitiesAsUser(pkgName,
                     mActivityManager.getCurrentUser());
         } catch (NameNotFoundException e) {
             return null;
         }
     }
 
+    private String[] findDistractionOptimizedActivitiesAsUser(String pkgName, int userId)
+            throws NameNotFoundException {
+        String regionString;
+        synchronized (mLock) {
+            regionString = mCurrentDrivingSafetyRegion;
+        }
+        return CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(mContext, pkgName,
+                userId, regionString);
+    }
+
     /**
      * Reading policy and setting policy can take time. Run it in a separate handler thread.
      */
diff --git a/service/src/com/android/car/power/SilentModeHandler.java b/service/src/com/android/car/power/SilentModeHandler.java
index 2631c01..bb78810 100644
--- a/service/src/com/android/car/power/SilentModeHandler.java
+++ b/service/src/com/android/car/power/SilentModeHandler.java
@@ -39,9 +39,9 @@
  * Class to handle Silent Mode and Non-Silent Mode.
  *
  * <p>This monitors {@code /sys/power/pm_silentmode_hw_state} to figure out when to switch to Silent
- * Mode and updates {@code /sys/power/pm_silentmode_kernel} to tell early-init services about Silent
- * Mode change. Also, it handles forced Silent Mode for testing purpose, which is given through
- * reboot reason.
+ * Mode and updates {@code /sys/power/pm_silentmode_kernel_state} to tell early-init services about
+ * Silent Mode change. Also, it handles forced Silent Mode for testing purpose, which is given
+ * through reboot reason.
  */
 final class SilentModeHandler {
     static final String SILENT_MODE_FORCED_SILENT = "forced-silent";
@@ -53,7 +53,7 @@
     private static final String SYSFS_FILENAME_HW_STATE_MONITORING =
             "/sys/power/pm_silentmode_hw_state";
     private static final String SYSFS_FILENAME_KERNEL_SILENTMODE =
-            "/sys/power/pm_silentmode_kernel";
+            "/sys/power/pm_silentmode_kernel_state";
     private static final String VALUE_SILENT_MODE = "1";
     private static final String VALUE_NON_SILENT_MODE = "0";
     private static final String SYSTEM_BOOT_REASON = "sys.boot.reason";
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index af573f7..8f9f2d2 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -50,6 +50,7 @@
     <uses-permission android:name="android.car.permission.READ_CAR_STEERING"/>
     <uses-permission android:name="android.car.permission.STORAGE_MONITORING"/>
     <uses-permission android:name="android.car.permission.CAR_DYNAMICS_STATE"/>
+    <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING"/>
     <!-- Allow querying and writing to any property -->
     <uses-permission android:name="android.car.permission.CAR_ENERGY_PORTS" />
     <uses-permission android:name="android.car.permission.PERMISSION_CONTROL_ENERGY_PORTS" />
diff --git a/tests/android_car_api_test/AndroidManifest.xml b/tests/android_car_api_test/AndroidManifest.xml
index 7ae6fde..3e91a40 100644
--- a/tests/android_car_api_test/AndroidManifest.xml
+++ b/tests/android_car_api_test/AndroidManifest.xml
@@ -34,6 +34,44 @@
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
         </activity>
+        <activity android:name=".TestDrivingSafetyAllRegionActivity"
+                  android:exported="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyExplicitAllRegionsActivity"
+                  android:exported="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
+            <!-- not necessary as all region is the default state but this is still valid -->
+            <meta-data android:name="android.car.drivingsafetyregions"
+                       android:value="android.car.drivingsafetyregion.all"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyOneRegionActivity"
+                  android:exported="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
+            <meta-data android:name="android.car.drivingsafetyregions"
+                       android:value="com.android.car.test.drivingsafetyregion.1"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyTwoRegionsActivity"
+                  android:exported="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
+            <meta-data android:name="android.car.drivingsafetyregions"
+                       android:value="com.android.car.test.drivingsafetyregion.1,com.android.car.test.drivingsafetyregion.2"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyRegion1OnlyActivity"
+                  android:exported="true">
+            <!--No distractionOptimized, so this app will be unsafe. -->
+            <meta-data android:name="android.car.drivingsafetyregions"
+                       android:value="com.android.test.drivingsafetyregion.1"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyRegionAllOnlyActivity"
+                  android:exported="true">
+            <!--No distractionOptimized, so this app will be unsafe. -->
+            <meta-data android:name="android.car.drivingsafetyregions"
+                       android:value="android.car.drivingsafetyregion.all"/>
+        </activity>
+        <activity android:name=".TestDrivingSafetyRegionNoMetadataActivity"
+                  android:exported="true">
+        </activity>
         <service android:name=".CarProjectionManagerTest$TestService"
              android:exported="true"/>
     </application>
diff --git a/tests/android_car_api_test/src/android/car/apitest/DrivingSafetyRegionTest.java b/tests/android_car_api_test/src/android/car/apitest/DrivingSafetyRegionTest.java
new file mode 100644
index 0000000..473a8b1
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/DrivingSafetyRegionTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.app.ActivityManager;
+import android.car.Car;
+import android.car.content.pm.CarPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+@SmallTest
+public class DrivingSafetyRegionTest extends CarApiTestBase {
+    private static final String REGION1 = "com.android.car.test.drivingsafetyregion.1";
+    private static final String REGION2 = "com.android.car.test.drivingsafetyregion.2";
+    private static final String REGION3 = "com.android.car.test.drivingsafetyregion.3";
+
+    private static final String TEST_PACKAGE_NAME = "android.car.apitest";
+
+    private CarPackageManager mCarPackageManager;
+    private String mOriginalDrivingSafetyRegion = null;
+
+    private final int mCurrentUser = ActivityManager.getCurrentUser();
+
+    @Before
+    public void setUp() {
+        mCarPackageManager = (CarPackageManager) getCar().getCarManager(Car.PACKAGE_SERVICE);
+
+        assertThat(mCarPackageManager).isNotNull();
+
+        mOriginalDrivingSafetyRegion = mCarPackageManager.getCurrentDrivingSafetyRegion();
+
+        assertThat(mOriginalDrivingSafetyRegion).isNotNull();
+
+        // cannot run this in user build as region change is not allowed in user build for shell.
+        assumeTrue(Build.IS_ENG || Build.IS_USERDEBUG);
+    }
+
+    @After
+    public void tearDown() {
+        if (mOriginalDrivingSafetyRegion != null) {
+            setDrivingSafetyRegion(mOriginalDrivingSafetyRegion);
+        }
+    }
+
+    @Test
+    public void testImplicitAllRegions() throws Exception {
+        doTestAllRegions(TestDrivingSafetyAllRegionActivity.class.getName());
+    }
+
+    @Test
+    public void testExplicitAllRegions() throws Exception {
+        doTestAllRegions(TestDrivingSafetyExplicitAllRegionsActivity.class.getName());
+    }
+
+    private void doTestAllRegions(String activityClassName) throws Exception {
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        List<String> regions = mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                TEST_PACKAGE_NAME, activityClassName, ActivityManager.getCurrentUser());
+
+        assertThat(regions).containsExactly(CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+
+        // all region app should be safe always regardless of bypassing / region change
+        setDrivingSafetyRegion(REGION1);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, true, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, false, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+    }
+
+    @Test
+    public void testOneRegionOnly() throws Exception {
+        String activityClassName = TestDrivingSafetyOneRegionActivity.class.getName();
+
+        List<String> regions = mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                TEST_PACKAGE_NAME, activityClassName, ActivityManager.getCurrentUser());
+
+        assertThat(regions).containsExactly(REGION1);
+
+        setDrivingSafetyRegion(REGION1);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        setDrivingSafetyRegion(REGION2);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, true, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, false, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        setDrivingSafetyRegion(CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+    }
+
+    @Test
+    public void testTwoRegionsOnly() throws Exception {
+        String activityClassName = TestDrivingSafetyTwoRegionsActivity.class.getName();
+
+        List<String> regions = mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                TEST_PACKAGE_NAME, activityClassName, ActivityManager.getCurrentUser());
+
+        assertThat(regions).containsExactly(REGION1, REGION2);
+
+        setDrivingSafetyRegion(REGION1);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        setDrivingSafetyRegion(REGION2);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        setDrivingSafetyRegion(REGION3);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        setDrivingSafetyRegion(CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+    }
+
+    @Test
+    public void testRegion1OnlyActivity() throws Exception {
+        doTestRegionOnlyOrNoRegionCase(TestDrivingSafetyRegion1OnlyActivity.class.getName());
+    }
+
+    @Test
+    public void testRegionAllOnlyActivity() throws Exception {
+        doTestRegionOnlyOrNoRegionCase(TestDrivingSafetyRegionAllOnlyActivity.class.getName());
+    }
+
+    @Test
+    public void testRegionNoMetadataActivity() throws Exception {
+        doTestRegionOnlyOrNoRegionCase(TestDrivingSafetyRegionNoMetadataActivity.class.getName());
+    }
+
+    private void doTestRegionOnlyOrNoRegionCase(String activityClassName) throws Exception {
+        List<String> regions = mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                TEST_PACKAGE_NAME, activityClassName, ActivityManager.getCurrentUser());
+
+        // not distraction optimized, so list should be empty.
+        assertThat(regions).isEmpty();
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        // should not be safe for any region.
+        setDrivingSafetyRegion(CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        setDrivingSafetyRegion(REGION1);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        setDrivingSafetyRegion(REGION2);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, true, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isTrue();
+
+        mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(TEST_PACKAGE_NAME,
+                activityClassName, false, mCurrentUser);
+
+        assertThat(mCarPackageManager.isActivityDistractionOptimized(TEST_PACKAGE_NAME,
+                activityClassName)).isFalse();
+    }
+
+    @Test
+    public void testNoPackage() {
+        String noPkg = "NoSuchPackage";
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                        noPkg, "", mCurrentUser));
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(
+                        noPkg, "", true, mCurrentUser));
+    }
+
+    @Test
+    public void testNoActivity() {
+        String noSuchActivity = "NoSuchActivity";
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> mCarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser(
+                        TEST_PACKAGE_NAME, noSuchActivity, mCurrentUser));
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> mCarPackageManager.controlTemporaryActivityBlockingBypassingAsUser(
+                        TEST_PACKAGE_NAME, noSuchActivity, true, mCurrentUser));
+    }
+
+    @Test
+    public void testResetEmptyRegion() {
+        setDrivingSafetyRegion(REGION1);
+
+        assertThat(mCarPackageManager.getCurrentDrivingSafetyRegion()).isEqualTo(REGION1);
+
+        // no arg means all
+        runShellCommand("cmd car_service set-drivingsafety-region");
+
+        assertThat(mCarPackageManager.getCurrentDrivingSafetyRegion()).isEqualTo(
+                CarPackageManager.DRIVING_SAFETY_REGION_ALL);
+    }
+
+    private void setDrivingSafetyRegion(String region) {
+        runShellCommand("cmd car_service set-drivingsafety-region  " + region);
+    }
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyAllRegionActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyAllRegionActivity.java
new file mode 100644
index 0000000..48af4dd
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyAllRegionActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyAllRegionActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyExplicitAllRegionsActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyExplicitAllRegionsActivity.java
new file mode 100644
index 0000000..f369acc
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyExplicitAllRegionsActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyExplicitAllRegionsActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyOneRegionActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyOneRegionActivity.java
new file mode 100644
index 0000000..623e5e0
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyOneRegionActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyOneRegionActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegion1OnlyActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegion1OnlyActivity.java
new file mode 100644
index 0000000..2256330
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegion1OnlyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyRegion1OnlyActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionAllOnlyActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionAllOnlyActivity.java
new file mode 100644
index 0000000..863d308
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionAllOnlyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyRegionAllOnlyActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionNoMetadataActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionNoMetadataActivity.java
new file mode 100644
index 0000000..e633b6c
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyRegionNoMetadataActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyRegionNoMetadataActivity extends Activity {
+}
diff --git a/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyTwoRegionsActivity.java b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyTwoRegionsActivity.java
new file mode 100644
index 0000000..3124b81
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/TestDrivingSafetyTwoRegionsActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.apitest;
+
+import android.app.Activity;
+
+public class TestDrivingSafetyTwoRegionsActivity extends Activity {
+}