[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: a53889d273 -s ours am: e08608b6c2 -s ours am: 300c9c4261 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Car/+/24979997
Change-Id: Idaace39dc4b9c485ca3a5242563d3862f8310963
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/FrameworkPackageStubs/Android.bp b/FrameworkPackageStubs/Android.bp
index ee6baa0..0088038 100644
--- a/FrameworkPackageStubs/Android.bp
+++ b/FrameworkPackageStubs/Android.bp
@@ -9,5 +9,8 @@
platform_apis: true,
+ // Required to allow non 0 intent filter priority
+ privileged: true,
+
certificate: "platform",
}
diff --git a/FrameworkPackageStubs/AndroidManifest.xml b/FrameworkPackageStubs/AndroidManifest.xml
index 683b9ad..1737a2e 100644
--- a/FrameworkPackageStubs/AndroidManifest.xml
+++ b/FrameworkPackageStubs/AndroidManifest.xml
@@ -98,10 +98,6 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter android:priority="-1">
- <action android:name="android.settings.MANAGE_UNKNOWN_APP_SOURCES"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <intent-filter android:priority="-1">
<action android:name="android.settings.PROCESS_WIFI_EASY_CONNECT_URI"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="DPP"/>
@@ -112,6 +108,26 @@
</intent-filter>
</activity>
+ <!-- These disable the Settings UI to enable a trusted external source
+ to install unknown apks. Set android:priority="101" to avoid other
+ apps to take the priority, nor conflicting with Settings' 1 -->
+ <activity android:name=".Stubs$ManageExternalSourcesActivityStub"
+ android:label="@string/stub_name"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <!-- ManageExternalSourcesActivity stub -->
+ <intent-filter android:priority="101">
+ <action android:name="android.settings.MANAGE_UNKNOWN_APP_SOURCES"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <!-- ManageAppExternalSourcesActivity stub when a package is specified -->
+ <intent-filter android:priority="101">
+ <action android:name="android.settings.MANAGE_UNKNOWN_APP_SOURCES"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="package" />
+ </intent-filter>
+ </activity>
+
<!-- CDD Core Application Intents Stubs -->
<!-- Desk Clock -->
<activity android:name=".Stubs$DeskClockStub"
@@ -250,5 +266,65 @@
</intent-filter>
</activity>
+ <!-- DocumentsUI stub to handle app file access requests.
+ For devices planning to properly support File Management,
+ DocumentsUIStubWithResult & DocumentsUIStub should be removed. -->
+ <activity
+ android:name=".Stubs$DocumentsUIStubWithResult"
+ android:label="@string/stub_name"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <!-- .picker.PickActivity
+ set android:priority="101" to avoid other apps to take the priority,
+ and in case of conflicting with DocumentsUI's 100. -->
+ <intent-filter android:priority="101">
+ <action android:name="android.intent.action.OPEN_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter android:priority="101">
+ <action android:name="android.intent.action.CREATE_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter android:priority="101">
+ <action android:name="android.intent.action.GET_CONTENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter android:priority="101">
+ <action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".Stubs$DocumentsUIStub"
+ android:label="@string/stub_name"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <!-- .files.FilesActivity
+ For VIEW actions, ComponentResolver always adjustPriority to 0. -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.document/root" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.document/directory" />
+ </intent-filter>
+ <!-- .ViewDownloadsActivity
+ Set android:priority="101" to avoid other apps to take the priority,
+ nor conflicting with DocumentsUI's 0. -->
+ <intent-filter android:priority="101">
+ <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/FrameworkPackageStubs/README.md b/FrameworkPackageStubs/README.md
new file mode 100644
index 0000000..4c62439
--- /dev/null
+++ b/FrameworkPackageStubs/README.md
@@ -0,0 +1,37 @@
+# Car FrameworkPackageStubs
+
+Car FrameworkPackageStubs handles certain [Common intents] and informs users
+they are not supported by android.hardware.type.automotive devices. This keeps
+users informed, simplifies app development and avoids app crashing even when
+apps do not check resolveActivity() first nor handle ActivityNotFoundException.
+
+## How to add common intents
+
+The steps are:
+
+1. Ensure the intents should be no-ops according to CDD, CTS, developer guides,
+ etc.
+2. Add the same activity intent-filter to the AndroidManifest.xml as them in the
+ original package.
+3. Add a stub class in the Stubs.java to show the toast. You may also customize
+ the message as needed.
+4. Remove the original package handling those intents from the build targets.
+5. Add & pass [CarFrameworkPackageStubsTest].
+6. Validate the build targets can pass the relevant CTS and sample apps will not
+ crash.
+
+## References
+
+1. CDD: [3.2.3.1. Common Application Intents]
+2. CTS: [AvailableIntentsTest.java]
+3. Developer guides: [Common Intents (API 31)]
+
+[CarFrameworkPackageStubsTest]: ../tests/CarFrameworkPackageStubsTest/README.md
+
+[Common intents]: https://developer.android.com/guide/components/intents-common
+
+[3.2.3.1. Common Application Intents]: https://source.android.com/docs/compatibility/12/android-12-cdd#3231_common_application_intents
+
+[AvailableIntentsTest.java]: ../../../../cts/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+
+[Common Intents (API 31)]: https://developer.android.com/about/versions/12/reference/common-intents-31
diff --git a/FrameworkPackageStubs/res/values/strings.xml b/FrameworkPackageStubs/res/values/strings.xml
index 472c85a..af92311 100644
--- a/FrameworkPackageStubs/res/values/strings.xml
+++ b/FrameworkPackageStubs/res/values/strings.xml
@@ -10,6 +10,12 @@
<!-- Toast message displayed for pip settings when pip isn't supported. [CHAR LIMIT=NONE] -->
<string name="pip_not_supported">Picture in Picture is not supported on this device</string>
+ <!-- Toast message displayed when MANAGE_UNKNOWN_APP_SOURCES isn't supported. [CHAR LIMIT=NONE] -->
+ <string name="manage_unknown_app_sources_not_supported">Manage Unknown App Sources is not supported on this device</string>
+
+ <!-- Toast message displayed when documentsui isn't supported. [CHAR LIMIT=NONE] -->
+ <string name="documentsui_not_supported">File Management is not supported on this device</string>
+
<!-- Stub name [CHAR LIMIT=NONE]-->
<string name="stub_name">None</string>
diff --git a/FrameworkPackageStubs/src/com/android/car/frameworkpackagestubs/Stubs.java b/FrameworkPackageStubs/src/com/android/car/frameworkpackagestubs/Stubs.java
index 58d0261..470579d 100644
--- a/FrameworkPackageStubs/src/com/android/car/frameworkpackagestubs/Stubs.java
+++ b/FrameworkPackageStubs/src/com/android/car/frameworkpackagestubs/Stubs.java
@@ -24,8 +24,10 @@
/**
* Contains different stub classes.
*
- * <p>These are broken out so that the intent filters are easier to track and so that
- * individual ones can create more specific messages if desired.
+ * <p>This handles the intents to avoid app crashing and notify the function is not supported.
+ *
+ * <p>These are broken out so that the intent filters are easier to track and so that individual
+ * ones can create more specific messages if desired.
*/
public final class Stubs {
@@ -34,7 +36,8 @@
*
* <p>Shows a toast and finishes.
*
- * <p>Subclasses can override {@link #getMessage()} to customize the message.
+ * <p>Subclasses can override {@link #getMessage()} to customize the message. Subclasses can
+ * override {@link #setResultImp()} if to return a result.
*/
private static class BaseActivity extends Activity {
@@ -44,6 +47,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showToast();
+ setResultImp();
finish();
}
@@ -51,6 +55,9 @@
return getResources().getString(R.string.message_not_supported);
}
+ /* No result is expected by default */
+ protected void setResultImp() {}
+
private void showToast() {
cancelToast();
mToast = Toast.makeText(this, getMessage(), Toast.LENGTH_LONG);
@@ -100,6 +107,23 @@
public static class SettingsStub extends BaseActivity { }
/**
+ * Stub activity for android.settings.MANAGE_UNKNOWN_APP_SOURCES intent. It returns
+ * RESULT_CANCELED for startActivityForResult() according to CDD Application Packaging
+ * Compatibility.
+ */
+ public static class ManageExternalSourcesActivityStub extends BaseActivity {
+ @Override
+ protected CharSequence getMessage() {
+ return getResources().getString(R.string.manage_unknown_app_sources_not_supported);
+ }
+
+ @Override
+ protected void setResultImp() {
+ setResult(RESULT_CANCELED);
+ }
+ }
+
+ /**
* Stub activity for ignore background data restriction setting.
*/
public static class IgnoreBackgroundDataRestrictionsSettingsStub extends BaseActivity { }
@@ -131,4 +155,20 @@
return super.getMessage();
}
}
+
+ /** Stub activity for DocumentsUI intents. */
+ public static class DocumentsUIStub extends BaseActivity {
+ @Override
+ protected CharSequence getMessage() {
+ return getResources().getString(R.string.documentsui_not_supported);
+ }
+ }
+
+ /** Stub activity for DocumentsUI intents expecting the result. */
+ public static class DocumentsUIStubWithResult extends DocumentsUIStub {
+ @Override
+ protected void setResultImp() {
+ setResult(RESULT_CANCELED);
+ }
+ }
}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 29da8fc..6c56789 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,10 +2,14 @@
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
overlayable_resource_hook = ${REPO_ROOT}/packages/apps/Car/systemlibs/tools/rro/verify-overlayable.py -r service/res -e service/res/values/overlayable.xml service/res/values/strings.xml service/res/values/attrs.xml service/res/xml/car_volume_groups.xml service/res/drawable/perm_group_car.xml service/res/values-h800dp/dimens.xml service/res/values-h1920dp/dimens.xml -o service/res/values/overlayable.xml -m 'service/res/values/overlayable.xml is not in sync with service/res/values/config.xml. You have added/removed config resources in config.xml. Please update Overlayable.xml so that it is sync with config.xml'
-annotation_classlist_repohook = ${REPO_ROOT}/packages/services/Car/tools/GenericCarApiBuilder/annotation_classlist_repohook.py ${REPO_ROOT}
+# Annotation and any Car mainline related checks are disabled.
+# annotation_classlist_repohook = ${REPO_ROOT}/packages/services/Car/tools/GenericCarApiBuilder/annotation_classlist_repohook.py ${REPO_ROOT}
[Builtin Hooks]
commit_msg_changeid_field = true
commit_msg_test_field = true
cpplint = true
clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/apex_car_framework/Android.bp b/apex_car_framework/Android.bp
index 296f748..30dfd71 100644
--- a/apex_car_framework/Android.bp
+++ b/apex_car_framework/Android.bp
@@ -38,7 +38,8 @@
compile_multilib: "both",
name: "com.android.car.framework-defaults",
- min_sdk_version: "33",
+ // TODO(b/288271411): enable it when car mainline module is supported
+ // min_sdk_version: "33",
file_contexts: ":com.android.car.framework-file_contexts",
key: "com.android.car.framework.key",
certificate: ":com.android.car.framework.certificate",
diff --git a/car-builtin-lib/api/module-lib-current.txt b/car-builtin-lib/api/module-lib-current.txt
index e564a43..65fcef3 100644
--- a/car-builtin-lib/api/module-lib-current.txt
+++ b/car-builtin-lib/api/module-lib-current.txt
@@ -19,6 +19,7 @@
enum_constant public static final android.car.builtin.annotation.PlatformVersion TIRAMISU_2;
enum_constant public static final android.car.builtin.annotation.PlatformVersion TIRAMISU_3;
enum_constant public static final android.car.builtin.annotation.PlatformVersion UPSIDE_DOWN_CAKE_0;
+ enum_constant public static final android.car.builtin.annotation.PlatformVersion UPSIDE_DOWN_CAKE_1;
}
}
@@ -65,6 +66,7 @@
public class TaskInfoHelper {
method @RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @NonNull public static android.graphics.Rect getBounds(@NonNull android.app.TaskInfo);
method public static int getDisplayId(@NonNull android.app.TaskInfo);
+ method public static android.os.IBinder getToken(@NonNull android.app.TaskInfo);
method public static int getUserId(@NonNull android.app.TaskInfo);
method public static boolean isVisible(@NonNull android.app.TaskInfo);
method public static String toString(@Nullable android.app.TaskInfo);
diff --git a/car-builtin-lib/src/android/car/builtin/annotation/PlatformVersion.java b/car-builtin-lib/src/android/car/builtin/annotation/PlatformVersion.java
index 2fcfac7..273401a 100644
--- a/car-builtin-lib/src/android/car/builtin/annotation/PlatformVersion.java
+++ b/car-builtin-lib/src/android/car/builtin/annotation/PlatformVersion.java
@@ -32,5 +32,6 @@
TIRAMISU_2,
TIRAMISU_3,
UPSIDE_DOWN_CAKE_0,
+ UPSIDE_DOWN_CAKE_1,
// DO NOT ADD minor UPSIDE_DOWN_CAKE version until lint tool is working. (b/275125924)
}
diff --git a/car-builtin-lib/src/android/car/builtin/app/TaskInfoHelper.java b/car-builtin-lib/src/android/car/builtin/app/TaskInfoHelper.java
index 1099f5b..a9ac406 100644
--- a/car-builtin-lib/src/android/car/builtin/app/TaskInfoHelper.java
+++ b/car-builtin-lib/src/android/car/builtin/app/TaskInfoHelper.java
@@ -25,6 +25,7 @@
import android.car.builtin.annotation.PlatformVersion;
import android.graphics.Rect;
import android.os.Build;
+import android.os.IBinder;
/**
* Provides the access to the hidden fields of {@code android.app.TaskInfo}.
@@ -51,6 +52,12 @@
return task.isVisible && task.isRunning && !task.isSleeping;
}
+ /** Returns the binder token of the task. */
+ @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public static IBinder getToken(@NonNull TaskInfo task) {
+ return task.token.asBinder();
+ }
+
/** Returns the string representation of the task */
@AddedIn(PlatformVersion.TIRAMISU_0)
public static String toString(@Nullable TaskInfo task) {
diff --git a/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java b/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
index c798ee3..2d4ed17 100644
--- a/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
+++ b/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
@@ -26,13 +26,22 @@
import com.android.internal.util.Preconditions;
import com.android.car.internal.evs.GLES20CarEvsBufferRenderer;
+import java.util.ArrayList;
+
/**
* GPU-backed SurfaceView to render a hardware buffer described by {@link CarEvsBufferDescriptor}.
*/
public final class CarEvsGLSurfaceView extends GLSurfaceView {
private static final String TAG = CarEvsGLSurfaceView.class.getSimpleName();
private static final int DEFAULT_IN_PLANE_ROTATION_ANGLE = 0;
-
+ private static final float DEFAULT_1X1_POSITION[][] = {
+ {
+ -1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f,
+ },
+ };
private final GLES20CarEvsBufferRenderer mRenderer;
/** An interface to pull and return {@code CarEvsBufferDescriptor} object to render. */
@@ -55,11 +64,12 @@
void onBufferProcessed(@NonNull CarEvsBufferDescriptor buffer);
}
- private CarEvsGLSurfaceView(Context context, BufferCallback callback, int angleInDegree) {
+ private CarEvsGLSurfaceView(Context context, ArrayList<BufferCallback> callbacks,
+ int angleInDegree, float[][] positions) {
super(context);
setEGLContextClientVersion(2);
- mRenderer = new GLES20CarEvsBufferRenderer(context, callback, angleInDegree);
+ mRenderer = new GLES20CarEvsBufferRenderer(callbacks, angleInDegree, positions);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
@@ -75,27 +85,66 @@
/**
* Creates a {@link CarEvsGLSurfaceView} object with the default in-plane rotation angle.
*
- * @param context Current appliation context.
- * @param callback {@link CarEvsGLSurfaceView.BufferCallback} object.
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
*
*/
- public static CarEvsGLSurfaceView create(Context context, BufferCallback callback) {
- return create(context, callback, DEFAULT_IN_PLANE_ROTATION_ANGLE);
+ public static CarEvsGLSurfaceView create(Context context,
+ ArrayList<BufferCallback> callbacks) {
+ return create(context, callbacks, DEFAULT_IN_PLANE_ROTATION_ANGLE, DEFAULT_1X1_POSITION);
}
/**
* Creates a {@link CarEvsGLSurfaceView} object with a given in-plane rotation angle in degree.
*
- * @param context Current appliation context.
- * @param callback {@link CarEvsGLSurfaceView.BufferCallback} object.
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
* @param angleInDegree In-plane counter-clockwise rotation angle in degree.
+ *
*/
- public static CarEvsGLSurfaceView create(Context context, BufferCallback callback,
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
int angleInDegree) {
+ return create(context, callbacks, angleInDegree, DEFAULT_1X1_POSITION);
+ }
+
+ /**
+ * Creates a {@link CarEvsGLSurfaceView} object with the default in-plane rotation angle and a
+ * gridview with given rows and columns.
+ *
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
+ * @param positions Matrices that define an area where each buffer will be rendered.
+ *
+ */
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
+ float positions[][]) {
+ return create(context, callbacks, DEFAULT_IN_PLANE_ROTATION_ANGLE, positions);
+ }
+
+ /**
+ * Creates a {@link CarEvsGLSurfaceView} object with a given in-plane rotation angle in degree
+ * and a gridview with given rows and columns.
+ *
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
+ * @param angleInDegree In-plane counter-clockwise rotation angle in degree.
+ * @param positions Matrices that define an area where each buffer will be rendered.
+ *
+ */
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
+ int angleInDegree, float positions[][]) {
Preconditions.checkArgument(context != null, "Context cannot be null.");
- Preconditions.checkArgument(callback != null, "BufferCallback cannot be null.");
+ Preconditions.checkArgument(callbacks != null, "BufferCallback cannot be null.");
+ Preconditions.checkArgument(callbacks.size() > 0,
+ "At least one BufferCallback object must exist.");
+ Preconditions.checkArgument(callbacks.size() <= positions.length,
+ "At least " + callbacks.size() + " positions are needed.");
- return new CarEvsGLSurfaceView(context, callback, angleInDegree);
+ return new CarEvsGLSurfaceView(context, callbacks, angleInDegree, positions);
}
}
diff --git a/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java b/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
index 8de69f1..be79d71 100644
--- a/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
+++ b/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.car.evs.CarEvsBufferDescriptor;
-import android.content.Context;
import android.hardware.HardwareBuffer;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
@@ -32,6 +31,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
+import java.util.ArrayList;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -46,13 +46,13 @@
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final int FLOAT_SIZE_BYTES = 4;
- private static final float[] sVertCarPosData = {
+ private static final float[] sVertPosData = {
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f };
- private static final float[] sVertCarTexData = {
+ private static final float[] sVertTexData = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
@@ -64,7 +64,7 @@
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
- private final String mVertexShader =
+ private static final String mVertexShader =
"attribute vec4 pos; \n" +
"attribute vec2 tex; \n" +
"uniform mat4 cameraMat; \n" +
@@ -75,7 +75,7 @@
" uv = tex; \n" +
"} \n";
- private final String mFragmentShader =
+ private static final String mFragmentShader =
"precision mediump float; \n" +
"uniform sampler2D tex; \n" +
"varying vec2 uv; \n" +
@@ -84,154 +84,169 @@
" gl_FragColor = texture2D(tex, uv); \n" +
"} \n";
+ private static final int INDEX_TO_X_STRIDE = 0;
+ private static final int INDEX_TO_Y_STRIDE = 1;
+
private final Object mLock = new Object();
- private final CarEvsGLSurfaceView.BufferCallback mCallback;
- private final Context mContext;
- private final FloatBuffer mVertCarPos;
- private final FloatBuffer mVertCarTex;
+ private final ArrayList<CarEvsGLSurfaceView.BufferCallback> mCallbacks;
+ private final FloatBuffer mVertPos;
+ private final FloatBuffer mVertTex;
+ private final int mTextureId[];
+ private final float mPositions[][];
private int mProgram;
- private int mTextureId;
private int mWidth;
private int mHeight;
- // Native method to update the texture with a received frame buffer
+ // Hold buffers currently in use.
@GuardedBy("mLock")
- private CarEvsBufferDescriptor mBufferInUse;
+ private final CarEvsBufferDescriptor mBufferInUse[];
/** Load jni on initialization. */
static {
System.loadLibrary("carevsglrenderer_jni");
}
- public GLES20CarEvsBufferRenderer(@NonNull Context context,
- @NonNull CarEvsGLSurfaceView.BufferCallback callback, int angleInDegree) {
+ public GLES20CarEvsBufferRenderer(ArrayList<CarEvsGLSurfaceView.BufferCallback> callbacks,
+ int angleInDegree, float[][] positions) {
- Preconditions.checkArgument(context != null, "Context cannot be null.");
- Preconditions.checkArgument(callback != null, "Callback cannot be null.");
+ Preconditions.checkArgument(callbacks != null, "Callback cannot be null.");
+ Preconditions.checkArgument(callbacks.size() <= positions.length,
+ "At least " + callbacks.size() + " positions are needed.");
- mContext = context;
- mCallback = callback;
+ mCallbacks = callbacks;
- mVertCarPos = ByteBuffer.allocateDirect(sVertCarPosData.length * FLOAT_SIZE_BYTES)
- .order(ByteOrder.nativeOrder()).asFloatBuffer();
- mVertCarPos.put(sVertCarPosData).position(0);
+ mTextureId = new int[callbacks.size()];
+ mPositions = positions;
+ mBufferInUse = new CarEvsBufferDescriptor[callbacks.size()];
+
+ mVertPos = ByteBuffer.allocateDirect(sVertPosData.length * FLOAT_SIZE_BYTES)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mVertPos.put(sVertPosData).position(0);
double angleInRadian = Math.toRadians(angleInDegree);
float[] rotated = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
float sin = (float)Math.sin(angleInRadian);
float cos = (float)Math.cos(angleInRadian);
- rotated[0] += cos * sVertCarTexData[0] - sin * sVertCarTexData[1];
- rotated[1] += sin * sVertCarTexData[0] + cos * sVertCarTexData[1];
- rotated[2] += cos * sVertCarTexData[2] - sin * sVertCarTexData[3];
- rotated[3] += sin * sVertCarTexData[2] + cos * sVertCarTexData[3];
- rotated[4] += cos * sVertCarTexData[4] - sin * sVertCarTexData[5];
- rotated[5] += sin * sVertCarTexData[4] + cos * sVertCarTexData[5];
- rotated[6] += cos * sVertCarTexData[6] - sin * sVertCarTexData[7];
- rotated[7] += sin * sVertCarTexData[6] + cos * sVertCarTexData[7];
+ rotated[0] += cos * sVertTexData[0] - sin * sVertTexData[1];
+ rotated[1] += sin * sVertTexData[0] + cos * sVertTexData[1];
+ rotated[2] += cos * sVertTexData[2] - sin * sVertTexData[3];
+ rotated[3] += sin * sVertTexData[2] + cos * sVertTexData[3];
+ rotated[4] += cos * sVertTexData[4] - sin * sVertTexData[5];
+ rotated[5] += sin * sVertTexData[4] + cos * sVertTexData[5];
+ rotated[6] += cos * sVertTexData[6] - sin * sVertTexData[7];
+ rotated[7] += sin * sVertTexData[6] + cos * sVertTexData[7];
- mVertCarTex = ByteBuffer.allocateDirect(sVertCarTexData.length * FLOAT_SIZE_BYTES)
+ mVertTex = ByteBuffer.allocateDirect(sVertTexData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
- mVertCarTex.put(rotated).position(0);
+ mVertTex.put(rotated).position(0);
}
public void clearBuffer() {
- CarEvsBufferDescriptor bufferToReturn = null;
- synchronized (mLock) {
- if (mBufferInUse == null) {
- return;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ CarEvsBufferDescriptor bufferToReturn = null;
+ synchronized (mLock) {
+ if (mBufferInUse[i] == null) {
+ continue;
+ }
+
+ bufferToReturn = mBufferInUse[i];
+ mBufferInUse[i] = null;
}
- bufferToReturn = mBufferInUse;
- mBufferInUse = null;
+ // bufferToReturn is not null here.
+ mCallbacks.get(i).onBufferProcessed(bufferToReturn);
}
-
- // bufferToReturn is not null here.
- mCallback.onBufferProcessed(bufferToReturn);
}
@Override
public void onDrawFrame(GL10 glUnused) {
// Use the GLES20 class's static methods instead of a passed GL10 interface.
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ CarEvsBufferDescriptor bufferToRender = null;
+ CarEvsBufferDescriptor bufferToReturn = null;
+ CarEvsBufferDescriptor newFrame = mCallbacks.get(i).onBufferRequested();
- CarEvsBufferDescriptor bufferToRender = null;
- CarEvsBufferDescriptor bufferToReturn = null;
- CarEvsBufferDescriptor newFrame = mCallback.onBufferRequested();
-
- synchronized (mLock) {
- if (newFrame != null) {
- // If a new frame has not been delivered yet, we're using a previous frame.
- if (mBufferInUse != null) {
- bufferToReturn = mBufferInUse;
+ synchronized (mLock) {
+ if (newFrame != null) {
+ if (mBufferInUse[i] != null) {
+ bufferToReturn = mBufferInUse[i];
+ }
+ mBufferInUse[i] = newFrame;
}
- mBufferInUse = newFrame;
+ bufferToRender = mBufferInUse[i];
}
- bufferToRender = mBufferInUse;
- }
- if (bufferToRender == null) {
- if (DBG) {
- Log.d(TAG, "No buffer to draw.");
+ if (bufferToRender == null) {
+ if (DBG) {
+ Log.d(TAG, "No buffer to draw from a callback " + i);
+ }
+ continue;
}
- return;
+
+ if (bufferToReturn != null) {
+ mCallbacks.get(i).onBufferProcessed(bufferToReturn);
+ }
+
+ // Specify a shader program to use
+ GLES20.glUseProgram(mProgram);
+
+ // Set a cameraMat as 4x4 identity matrix
+ int matrix = GLES20.glGetUniformLocation(mProgram, "cameraMat");
+ if (matrix < 0) {
+ throw new RuntimeException("Could not get a attribute location for cameraMat");
+ }
+ GLES20.glUniformMatrix4fv(/* location= */ matrix, /* count= */ 1,
+ /* transpose= */ false, /* value= */ sIdentityMatrix, 0);
+
+ // Retrieve a hardware buffer from a descriptor and update the texture
+ HardwareBuffer buffer = bufferToRender.getHardwareBuffer();
+
+ // Update the texture with a given hardware buffer
+ if (!nUpdateTexture(buffer, mTextureId[i])) {
+ throw new RuntimeException(
+ "Failed to update the texture with the preview frame");
+ }
}
- if (bufferToReturn != null) {
- mCallback.onBufferProcessed(bufferToReturn);
- }
-
- // Specify a shader program to use
- GLES20.glUseProgram(mProgram);
-
- // Set a cameraMat as 4x4 identity matrix
- int matrix = GLES20.glGetUniformLocation(mProgram, "cameraMat");
- if (matrix < 0) {
- throw new RuntimeException("Could not get a attribute location for cameraMat");
- }
- GLES20.glUniformMatrix4fv(matrix, 1, false, sIdentityMatrix, 0);
-
- // Retrieve a hardware buffer from a descriptor and update the texture
- HardwareBuffer buffer = bufferToRender.getHardwareBuffer();
-
- // Update the texture with a given hardware buffer
- if (!nUpdateTexture(buffer, mTextureId)) {
- throw new RuntimeException(
- "Failed to update the texture with the preview frame");
- }
-
- GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-
// Select active texture unit
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- // Bind a named texture to the target
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
+ // Render Hardware buffers
+ for (int i = 0; i < mTextureId.length; i++) {
+ mVertPos.put(mPositions[i]).position(0);
- // Use a texture slot 0 as the source
- int sampler = GLES20.glGetUniformLocation(mProgram, "tex");
- if (sampler < 0) {
- throw new RuntimeException("Could not get a attribute location for tex");
+ // Bind a named texture to the target
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId[i]);
+
+ // Use a texture slot 0 as the source
+ int sampler = GLES20.glGetUniformLocation(mProgram, "tex");
+ if (sampler < 0) {
+ throw new RuntimeException("Could not get a attribute location for tex");
+ }
+ GLES20.glUniform1i(sampler, 0);
+
+ // We'll ignore the alpha value
+ GLES20.glDisable(GLES20.GL_BLEND);
+
+ // Bind a named texture to the target
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId[i]);
+
+ // Define an array of generic vertex attribute data
+ GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertPos);
+ GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, mVertTex);
+
+ // Enable a generic vertex attribute array
+ GLES20.glEnableVertexAttribArray(0);
+ GLES20.glEnableVertexAttribArray(1);
+
+ // Render primitives from array data
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+
+ GLES20.glDisableVertexAttribArray(0);
+ GLES20.glDisableVertexAttribArray(1);
}
- GLES20.glUniform1i(sampler, 0);
-
- // We'll ignore the alpha value
- GLES20.glDisable(GLES20.GL_BLEND);
-
- // Define an array of generic vertex attribute data
- GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertCarPos);
- GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, mVertCarTex);
-
- // Enable a generic vertex attribute array
- GLES20.glEnableVertexAttribArray(0);
- GLES20.glEnableVertexAttribArray(1);
-
- // Render primitives from array data
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
- GLES20.glDisableVertexAttribArray(0);
- GLES20.glDisableVertexAttribArray(1);
// Wait until all GL execution is complete
GLES20.glFinish();
@@ -256,26 +271,26 @@
}
// Generate texture name
- int[] textures = new int[1];
- GLES20.glGenTextures(1, textures, 0);
- mTextureId = textures[0];
- if (mTextureId <= 0) {
- Log.e(TAG, "Did not get a texture handle");
- return;
- }
+ GLES20.glGenTextures(/* n= */ mTextureId.length, mTextureId, /* offset= */ 0);
+ for (var id : mTextureId) {
+ if (id <= 0) {
+ Log.e(TAG, "Did not get a texture handle, id=" + id);
+ continue;
+ }
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
- // Use a linear interpolation to upscale the texture
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- // Use a nearest-neighbor to downscale the texture
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- // Clamp s, t coordinates at the edges
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
- GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
- GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
+ // Use a linear interpolation to upscale the texture
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+ // Use a nearest-neighbor to downscale the texture
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ // Clamp s, t coordinates at the edges
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE);
+ }
}
private int loadShader(int shaderType, String source) {
diff --git a/car-helper-lib/res/drawable/car_internal_guest_user_icon.xml b/car-helper-lib/res/drawable/car_internal_guest_user_icon.xml
new file mode 100644
index 0000000..8f37058
--- /dev/null
+++ b/car-helper-lib/res/drawable/car_internal_guest_user_icon.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="96dp"
+ android:height="96dp"
+ android:viewportWidth="96"
+ android:viewportHeight="96">
+ <path
+ android:pathData="M48,48m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"
+ android:fillColor="@color/car_internal_guest_user_background_color"/>
+ <group>
+ <clip-path
+ android:pathData="M5,5h86v86h-86z"/>
+ <path
+ android:pathData="M48,46C55.919,46 62.333,39.568 62.333,31.667C62.333,23.748 55.919,17.333 48,17.333C40.081,17.333 33.667,23.748 33.667,31.667C33.667,39.568 40.081,46 48,46ZM19.333,73.5C19.333,73.5 29,86 48,86C67,86 76.667,73.5 76.667,73.5C76.667,46.272 19.333,46.272 19.333,73.5Z"
+ android:fillColor="@color/car_internal_guest_user_avatar_color"/>
+ </group>
+</vector>
diff --git a/car-helper-lib/res/values/colors.xml b/car-helper-lib/res/values/colors.xml
index 4fc44d1..56a3f3d 100644
--- a/car-helper-lib/res/values/colors.xml
+++ b/car-helper-lib/res/values/colors.xml
@@ -37,4 +37,7 @@
<color name="car_internal_user_background_icon_8">#2f211C</color><!-- orange -->
<color name="car_internal_user_icon_default_background_color">#131313</color>
+
+ <color name="car_internal_guest_user_avatar_color">#DADCE0</color>
+ <color name="car_internal_guest_user_background_color">#202124</color>
</resources>
diff --git a/car-helper-lib/src/com/android/car/internal/user/CarUserIconProvider.java b/car-helper-lib/src/com/android/car/internal/user/CarUserIconProvider.java
index 2745a96..12ca8b7 100644
--- a/car-helper-lib/src/com/android/car/internal/user/CarUserIconProvider.java
+++ b/car-helper-lib/src/com/android/car/internal/user/CarUserIconProvider.java
@@ -28,7 +28,6 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
import com.android.internal.util.UserIcons;
@@ -96,10 +95,15 @@
return userIconBitmap;
}
+ static Bitmap getGuestDefaultUserIcon(Resources resources) {
+ return UserIcons.convertToBitmap(
+ resources.getDrawable(R.drawable.car_internal_guest_user_icon, null));
+ }
+
@ColorInt
static int getUserNameIconColor(@NonNull Context context, @NonNull UserInfo userInfo) {
if (userInfo.isGuest()) {
- throw new IllegalArgumentException("Cannot get user background color for guest user");
+ return context.getColor(R.color.car_internal_guest_user_avatar_color);
}
return context.getColor(USER_NAME_ICON_COLORS[userInfo.id % USER_NAME_ICON_COLORS.length]);
}
@@ -107,14 +111,9 @@
@ColorInt
static int getUserBackgroundIconColor(@NonNull Context context, @NonNull UserInfo userInfo) {
if (userInfo.isGuest()) {
- throw new IllegalArgumentException("Cannot get user background color for guest user");
+ return context.getColor(R.color.car_internal_guest_user_background_color);
}
return context.getColor(
USER_BACKGROUND_ICON_COLORS[userInfo.id % USER_BACKGROUND_ICON_COLORS.length]);
}
-
- private static Bitmap getGuestDefaultUserIcon(Resources resources) {
- return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
- resources, /*userId=*/ UserHandle.USER_NULL, /*light=*/ false));
- }
}
diff --git a/car-helper-lib/src/com/android/car/internal/user/UserHelper.java b/car-helper-lib/src/com/android/car/internal/user/UserHelper.java
index d96570b..34cee9a 100644
--- a/car-helper-lib/src/com/android/car/internal/user/UserHelper.java
+++ b/car-helper-lib/src/com/android/car/internal/user/UserHelper.java
@@ -17,6 +17,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -116,7 +117,7 @@
*
* @hide
*/
- @NonNull
+ @Nullable
public static Bitmap assignDefaultIcon(@NonNull Context context, @NonNull UserHandle user) {
Preconditions.checkArgument(context != null, "Context cannot be null");
Preconditions.checkArgument(user != null, "User cannot be null");
@@ -129,7 +130,19 @@
userManager.setUserIcon(user.getIdentifier(), bitmap);
return bitmap;
}
-
+ /**
+ * Returns the default user icon for guest users.
+ *
+ * @param context Current application context
+ * @return Bitmap of the user icon.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Bitmap getGuestDefaultIcon(@NonNull Context context) {
+ Preconditions.checkArgument(context != null, "Context cannot be null");
+ return CarUserIconProvider.getGuestDefaultUserIcon(context.getResources());
+ }
/**
* Get the user icon color for a given user id. This should not be the guest user.
*
diff --git a/car-lib-module/api/current.txt b/car-lib-module/api/current.txt
index aca2ed7..d2508ca 100644
--- a/car-lib-module/api/current.txt
+++ b/car-lib-module/api/current.txt
@@ -177,6 +177,7 @@
field @NonNull public static final android.car.CarVersion TIRAMISU_2;
field @NonNull public static final android.car.CarVersion TIRAMISU_3;
field @NonNull public static final android.car.CarVersion UPSIDE_DOWN_CAKE_0;
+ field @NonNull public static final android.car.CarVersion UPSIDE_DOWN_CAKE_1;
}
@Deprecated public final class EvConnectorType {
@@ -230,6 +231,7 @@
field @NonNull public static final android.car.PlatformVersion TIRAMISU_2;
field @NonNull public static final android.car.PlatformVersion TIRAMISU_3;
field @NonNull public static final android.car.PlatformVersion UPSIDE_DOWN_CAKE_0;
+ field @NonNull public static final android.car.PlatformVersion UPSIDE_DOWN_CAKE_1;
}
public final class PlatformVersionMismatchException extends java.lang.UnsupportedOperationException implements android.os.Parcelable {
diff --git a/car-lib-module/api/system-current.txt b/car-lib-module/api/system-current.txt
index de997b5..97cec07 100644
--- a/car-lib-module/api/system-current.txt
+++ b/car-lib-module/api/system-current.txt
@@ -377,7 +377,7 @@
public final class CarActivityManager {
method @Nullable @RequiresPermission(android.car.Car.PERMISSION_MIRROR_DISPLAY) public android.os.IBinder createDisplayMirroringToken(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.os.IBinder createTaskMirroringToken(int);
- method @MainThread @RequiresPermission(android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
+ method @MainThread @RequiresPermission(allOf={android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks(int);
method public boolean isCarSystemUIProxyRegistered();
@@ -404,9 +404,9 @@
}
public final class CarTaskViewController {
- method @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
- method public void release();
- method public void showEmbeddedTasks();
+ method @MainThread @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
+ method @MainThread public void release();
+ method @MainThread public void showEmbeddedTasks();
}
public interface CarTaskViewControllerCallback {
@@ -426,12 +426,12 @@
}
public final class ControlledRemoteCarTaskView extends android.view.SurfaceView {
- method @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
+ method @MainThread @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
method @MainThread @Nullable public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
method @MainThread public boolean isInitialized();
method public void onAttachedToWindow();
method public void onDetachedFromWindow();
- method public void release();
+ method @MainThread public void release();
method @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void removeInsets(int, int);
method @MainThread public void setObscuredTouchRect(@NonNull android.graphics.Rect);
method @MainThread public void setObscuredTouchRegion(@NonNull android.graphics.Region);
diff --git a/car-lib-module/api/test-current.txt b/car-lib-module/api/test-current.txt
index 230003e..5254ceb 100644
--- a/car-lib-module/api/test-current.txt
+++ b/car-lib-module/api/test-current.txt
@@ -70,6 +70,7 @@
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion TIRAMISU_2;
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion TIRAMISU_3;
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion UPSIDE_DOWN_CAKE_0;
+ enum_constant public static final android.car.annotation.ApiRequirements.CarVersion UPSIDE_DOWN_CAKE_1;
}
public enum ApiRequirements.PlatformVersion {
@@ -79,6 +80,7 @@
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion TIRAMISU_2;
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion TIRAMISU_3;
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion UPSIDE_DOWN_CAKE_0;
+ enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion UPSIDE_DOWN_CAKE_1;
}
}
diff --git a/car-lib/Android.bp b/car-lib/Android.bp
index 52dc1b2..a8010fd 100644
--- a/car-lib/Android.bp
+++ b/car-lib/Android.bp
@@ -78,7 +78,8 @@
"framework-bluetooth",
],
installable: true,
- min_sdk_version: "33",
+ // TODO(b/288271411): enable it when car mainline module is supported
+ // min_sdk_version: "33",
sdk_version: "module_current",
}
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index aca2ed7..d2508ca 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -177,6 +177,7 @@
field @NonNull public static final android.car.CarVersion TIRAMISU_2;
field @NonNull public static final android.car.CarVersion TIRAMISU_3;
field @NonNull public static final android.car.CarVersion UPSIDE_DOWN_CAKE_0;
+ field @NonNull public static final android.car.CarVersion UPSIDE_DOWN_CAKE_1;
}
@Deprecated public final class EvConnectorType {
@@ -230,6 +231,7 @@
field @NonNull public static final android.car.PlatformVersion TIRAMISU_2;
field @NonNull public static final android.car.PlatformVersion TIRAMISU_3;
field @NonNull public static final android.car.PlatformVersion UPSIDE_DOWN_CAKE_0;
+ field @NonNull public static final android.car.PlatformVersion UPSIDE_DOWN_CAKE_1;
}
public final class PlatformVersionMismatchException extends java.lang.UnsupportedOperationException implements android.os.Parcelable {
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index de997b5..97cec07 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -377,7 +377,7 @@
public final class CarActivityManager {
method @Nullable @RequiresPermission(android.car.Car.PERMISSION_MIRROR_DISPLAY) public android.os.IBinder createDisplayMirroringToken(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.os.IBinder createTaskMirroringToken(int);
- method @MainThread @RequiresPermission(android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
+ method @MainThread @RequiresPermission(allOf={android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks(int);
method public boolean isCarSystemUIProxyRegistered();
@@ -404,9 +404,9 @@
}
public final class CarTaskViewController {
- method @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
- method public void release();
- method public void showEmbeddedTasks();
+ method @MainThread @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
+ method @MainThread public void release();
+ method @MainThread public void showEmbeddedTasks();
}
public interface CarTaskViewControllerCallback {
@@ -426,12 +426,12 @@
}
public final class ControlledRemoteCarTaskView extends android.view.SurfaceView {
- method @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
+ method @MainThread @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
method @MainThread @Nullable public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
method @MainThread public boolean isInitialized();
method public void onAttachedToWindow();
method public void onDetachedFromWindow();
- method public void release();
+ method @MainThread public void release();
method @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void removeInsets(int, int);
method @MainThread public void setObscuredTouchRect(@NonNull android.graphics.Rect);
method @MainThread public void setObscuredTouchRegion(@NonNull android.graphics.Region);
diff --git a/car-lib/api/test-current.txt b/car-lib/api/test-current.txt
index 18d0faf..24a8fba 100644
--- a/car-lib/api/test-current.txt
+++ b/car-lib/api/test-current.txt
@@ -429,6 +429,7 @@
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion TIRAMISU_2;
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion TIRAMISU_3;
enum_constant public static final android.car.annotation.ApiRequirements.CarVersion UPSIDE_DOWN_CAKE_0;
+ enum_constant public static final android.car.annotation.ApiRequirements.CarVersion UPSIDE_DOWN_CAKE_1;
}
public enum ApiRequirements.PlatformVersion {
@@ -438,6 +439,7 @@
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion TIRAMISU_2;
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion TIRAMISU_3;
enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion UPSIDE_DOWN_CAKE_0;
+ enum_constant public static final android.car.annotation.ApiRequirements.PlatformVersion UPSIDE_DOWN_CAKE_1;
}
}
@@ -447,7 +449,7 @@
public final class CarActivityManager {
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @Nullable @RequiresPermission(android.car.Car.PERMISSION_MIRROR_DISPLAY) public android.os.IBinder createDisplayMirroringToken(int);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.os.IBinder createTaskMirroringToken(int);
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread @RequiresPermission(android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread @RequiresPermission(allOf={android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void getCarTaskViewController(@NonNull android.app.Activity, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.CarTaskViewControllerCallback);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.TIRAMISU_1, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.TIRAMISU_0) @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks();
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.TIRAMISU_0) @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getVisibleTasks(int);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public boolean isCarSystemUIProxyRegistered();
@@ -474,9 +476,9 @@
}
public final class CarTaskViewController {
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public void release();
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public void showEmbeddedTasks();
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread @RequiresPermission(allOf={android.Manifest.permission.INJECT_EVENTS, android.Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional=true) public void createControlledRemoteCarTaskView(@NonNull android.car.app.ControlledRemoteCarTaskViewConfig, @NonNull java.util.concurrent.Executor, @NonNull android.car.app.ControlledRemoteCarTaskViewCallback);
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public void release();
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public void showEmbeddedTasks();
}
public interface CarTaskViewControllerCallback {
@@ -496,12 +498,12 @@
}
public final class ControlledRemoteCarTaskView extends android.view.SurfaceView {
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void addInsets(int, int, @NonNull android.graphics.Rect);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread @Nullable public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public boolean isInitialized();
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public void onAttachedToWindow();
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public void onDetachedFromWindow();
- method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) public void release();
+ method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public void release();
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @RequiresPermission(android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY) public void removeInsets(int, int);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public void setObscuredTouchRect(@NonNull android.graphics.Rect);
method @android.car.annotation.ApiRequirements(minCarVersion=android.car.annotation.ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, minPlatformVersion=android.car.annotation.ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) @MainThread public void setObscuredTouchRegion(@NonNull android.graphics.Region);
diff --git a/car-lib/src/android/car/CarVersion.java b/car-lib/src/android/car/CarVersion.java
index 67bb10e..ee6addb 100644
--- a/car-lib/src/android/car/CarVersion.java
+++ b/car-lib/src/android/car/CarVersion.java
@@ -81,6 +81,15 @@
public static final CarVersion UPSIDE_DOWN_CAKE_0 =
new CarVersion("UPSIDE_DOWN_CAKE_0", Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 0);
+ /**
+ * Helper object for first minor upgrade of Android 14.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = PlatformVersion.TIRAMISU_0)
+ @NonNull
+ public static final CarVersion UPSIDE_DOWN_CAKE_1 =
+ new CarVersion("UPSIDE_DOWN_CAKE_1", Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 1);
+
private VERSION_CODES() {
throw new UnsupportedOperationException("Only provide constants");
}
diff --git a/car-lib/src/android/car/PlatformVersion.java b/car-lib/src/android/car/PlatformVersion.java
index 2bcccb3..1f524ae 100644
--- a/car-lib/src/android/car/PlatformVersion.java
+++ b/car-lib/src/android/car/PlatformVersion.java
@@ -83,6 +83,15 @@
public static final PlatformVersion UPSIDE_DOWN_CAKE_0 =
new PlatformVersion("UPSIDE_DOWN_CAKE_0", Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 0);
+ /**
+ * Helper object for first minor upgrade of Android 14.
+ */
+ @ApiRequirements(minCarVersion = CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
+ @NonNull
+ public static final PlatformVersion UPSIDE_DOWN_CAKE_1 =
+ new PlatformVersion("UPSIDE_DOWN_CAKE_1", Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 1);
+
// DO NOT ADD minor UPSIDE_DOWN_CAKE version until lint tool is working. (b/275125924)
private VERSION_CODES() {
diff --git a/car-lib/src/android/car/VehiclePropertyIds.java b/car-lib/src/android/car/VehiclePropertyIds.java
index 5ff2496..702f80b 100644
--- a/car-lib/src/android/car/VehiclePropertyIds.java
+++ b/car-lib/src/android/car/VehiclePropertyIds.java
@@ -2117,13 +2117,11 @@
/**
* Property to feed H/W input events to android.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Integer[]} property type
- * </ul>
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
+ *
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
+ *
+ * Deprecated, use {@link android.car.input.CarInputManager} instead.
*/
@AddedInOrBefore(majorVersion = 33)
public static final int HW_KEY_INPUT = 289475088;
@@ -3234,27 +3232,16 @@
public static final int SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 356518804;
/**
- * @deprecated This property is deprecated because it is defined as type {@link
- * VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}, which means all seats use the same value. Use
- * {@link #SEAT_HEADREST_HEIGHT_POS_V2} instead which fixes this issue by being defined as type
+ * Headrest height position.
+ *
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
+ *
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
+ *
+ * @deprecated because it is defined as type {@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL},
+ * which means all seats use the same value. Use {@link #SEAT_HEADREST_HEIGHT_POS_V2} instead
+ * which fixes this issue by being defined as type
* {@link VehicleAreaType#VEHICLE_AREA_TYPE_SEAT}.
- *
- * <p>Headrest height position.
- *
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ_WRITE} or
- * {@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Integer} property type
- * </ul>
- *
- * <p>Required Permission:
- * <ul>
- * <li>Signature|Privileged permission "android.car.permission.CONTROL_CAR_SEATS" to read
- * and write property.
- * </ul>
*/
@Deprecated
@RequiresPermission(Car.PERMISSION_CONTROL_CAR_SEATS)
@@ -4251,20 +4238,11 @@
/**
* Vehicle Maps Service (VMS) message.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ_WRITE}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Object[]} property type
- * </ul>
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
*
- * <p>Required Permission:
- * <ul>
- * <li>Signature|Privileged permission "android.car.permission.VMS_PUBLISHER" or
- * Signature|Privileged permission "android.car.permission.VMS_SUBSCRIBER" to read and write
- * property.
- * </ul>
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
+ *
+ * Deprecated, use {@link android.car.vms.VmsClientManager} instead.
*/
@RequiresPermission(anyOf = {Car.PERMISSION_VMS_PUBLISHER, Car.PERMISSION_VMS_SUBSCRIBER})
@AddedInOrBefore(majorVersion = 33)
@@ -4309,22 +4287,11 @@
/**
* OBD2 Live Sensor Data.
*
- * <p>Reports a snapshot of the current (live) values of the OBD2 sensors available.
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Object[]} property type
- * </ul>
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
*
- * <p>Required Permission:
- * <ul>
- * <li>Signature|Privileged permission "android.car.permission.CAR_DIAGNOSTICS" to read
- * property.
- * <li>Property is not writable.
- * </ul>
+ * Deprecated, use {@link android.car.diagnostic.CarDiagnosticManager} instead.
*/
@RequiresPermission(Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL)
@AddedInOrBefore(majorVersion = 33)
@@ -4332,23 +4299,11 @@
/**
* OBD2 Freeze Frame Sensor Data.
*
- * <p>Reports a snapshot of the value of the OBD2 sensors available at the time that a fault
- * occurred and was detected.
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Object[]} property type
- * </ul>
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
*
- * <p>Required Permission:
- * <ul>
- * <li>Signature|Privileged permission "android.car.permission.CAR_DIAGNOSTICS" to read
- * property.
- * <li>Property is not writable.
- * </ul>
+ * Deprecated, use {@link android.car.diagnostic.CarDiagnosticManager} instead.
*/
@RequiresPermission(Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL)
@AddedInOrBefore(majorVersion = 33)
@@ -4356,20 +4311,11 @@
/**
* OBD2 Freeze Frame Information.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Object[]} property type
- * </ul>
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
*
- * <p>Required Permission:
- * <ul>
- * <li>Signature|Privileged permission "android.car.permission.CAR_DIAGNOSTICS" to read
- * property.
- * <li>Property is not writable.
- * </ul>
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
+ *
+ * Deprecated, use {@link android.car.diagnostic.CarDiagnosticManager} instead.
*/
@RequiresPermission(Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL)
@AddedInOrBefore(majorVersion = 33)
@@ -4377,23 +4323,11 @@
/**
* OBD2 Freeze Frame Clear.
*
- * <p>This property allows deletion of any of the freeze frames stored in
- * vehicle memory, as described by {@link #OBD2_FREEZE_FRAME_INFO}.
+ * <p>Not exposed through {@link android.car.hardware.property.CarPropertyManager}.
*
- * <p>Property Config:
- * <ul>
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_WRITE}
- * <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}
- * <li>{@link android.car.hardware.CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}
- * <li>{@code Object[]} property type
- * </ul>
+ * <p>Trying to get/set this property will cause {@link SecurityException}.
*
- * <p>Required Permission:
- * <ul>
- * <li>Property is not readable.
- * <li>Signature|Privileged permission "android.car.permission.CLEAR_CAR_DIAGNOSTICS" to write
- * property.
- * </ul>
+ * Deprecated, use {@link android.car.diagnostic.CarDiagnosticManager} instead.
*/
@RequiresPermission(Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR)
@AddedInOrBefore(majorVersion = 33)
diff --git a/car-lib/src/android/car/annotation/ApiRequirements.java b/car-lib/src/android/car/annotation/ApiRequirements.java
index aa687a8..c14f771 100644
--- a/car-lib/src/android/car/annotation/ApiRequirements.java
+++ b/car-lib/src/android/car/annotation/ApiRequirements.java
@@ -77,7 +77,8 @@
TIRAMISU_1(android.car.CarVersion.VERSION_CODES.TIRAMISU_1),
TIRAMISU_2(android.car.CarVersion.VERSION_CODES.TIRAMISU_2),
TIRAMISU_3(android.car.CarVersion.VERSION_CODES.TIRAMISU_3),
- UPSIDE_DOWN_CAKE_0(android.car.CarVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0);
+ UPSIDE_DOWN_CAKE_0(android.car.CarVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0),
+ UPSIDE_DOWN_CAKE_1(android.car.CarVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_1);
private final android.car.CarVersion mVersion;
@@ -100,7 +101,8 @@
TIRAMISU_1(android.car.PlatformVersion.VERSION_CODES.TIRAMISU_1),
TIRAMISU_2(android.car.PlatformVersion.VERSION_CODES.TIRAMISU_2),
TIRAMISU_3(android.car.PlatformVersion.VERSION_CODES.TIRAMISU_3),
- UPSIDE_DOWN_CAKE_0(android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0);
+ UPSIDE_DOWN_CAKE_0(android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0),
+ UPSIDE_DOWN_CAKE_1(android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_1);
private final android.car.PlatformVersion mVersion;
diff --git a/car-lib/src/android/car/app/CarActivityManager.java b/car-lib/src/android/car/app/CarActivityManager.java
index f2741af..9d35b68 100644
--- a/car-lib/src/android/car/app/CarActivityManager.java
+++ b/car-lib/src/android/car/app/CarActivityManager.java
@@ -16,6 +16,8 @@
package android.car.app;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+
import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
import android.annotation.IntDef;
@@ -25,15 +27,18 @@
import android.annotation.RequiresApi;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.UiContext;
import android.app.Activity;
import android.app.ActivityManager;
import android.car.Car;
import android.car.CarManagerBase;
import android.car.annotation.AddedInOrBefore;
import android.car.annotation.ApiRequirements;
+import android.car.user.CarUserManager;
import android.car.view.MirroredSurfaceView;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.Context;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
@@ -474,7 +479,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Car.PERMISSION_MANAGE_CAR_SYSTEM_UI)
+ @RequiresPermission(allOf = {Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, INTERACT_ACROSS_USERS})
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
@@ -483,15 +488,51 @@
@NonNull Activity hostActivity,
@NonNull Executor callbackExecutor,
@NonNull CarTaskViewControllerCallback carTaskViewControllerCallback) {
+ getCarTaskViewController(
+ hostActivity,
+ CarTaskViewControllerHostLifecycleFactory.forActivity(hostActivity),
+ callbackExecutor,
+ carTaskViewControllerCallback);
+ }
+
+ /**
+ * Gets the {@link CarTaskViewController} using the {@code carTaskViewControllerCallback}.
+ *
+ * This method is expected to be called when the container (host) is created. It will
+ * take care of freeing up the held resources when container is destroyed. If the container is
+ * recreated, this method should be called again after it gets created again.
+ *
+ * @param carTaskViewControllerCallback the callback which the client can use to monitor the
+ * lifecycle of the {@link CarTaskViewController}.
+ * @param hostContext the visual hostContext which the container (host) is associated with.
+ * @param callbackExecutor the executor which the {@code carTaskViewControllerCallback} will be
+ * executed on.
+ * @param carTaskViewControllerHostLifecycle the lifecycle of the container (host).
+ * @hide
+ */
+ // TODO(b/293297847): Expose this as system API
+ @RequiresPermission(allOf = {Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, INTERACT_ACROSS_USERS})
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @MainThread
+ public void getCarTaskViewController(
+ @UiContext @NonNull Context hostContext,
+ @NonNull CarTaskViewControllerHostLifecycle carTaskViewControllerHostLifecycle,
+ @NonNull Executor callbackExecutor,
+ @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback) {
assertPlatformVersionAtLeastU();
try {
if (mCarTaskViewControllerSupervisor == null) {
// Same supervisor is used for multiple activities.
mCarTaskViewControllerSupervisor = new CarTaskViewControllerSupervisor(mService,
- getContext().getMainExecutor());
+ getContext().getMainExecutor(), mCar.getCarManager(CarUserManager.class));
}
- mCarTaskViewControllerSupervisor.createCarTaskViewController(callbackExecutor,
- carTaskViewControllerCallback, hostActivity);
+ mCarTaskViewControllerSupervisor.createCarTaskViewController(
+ hostContext,
+ carTaskViewControllerHostLifecycle,
+ callbackExecutor,
+ carTaskViewControllerCallback);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
diff --git a/car-lib/src/android/car/app/CarSystemUIProxy.java b/car-lib/src/android/car/app/CarSystemUIProxy.java
index d70c2c1..73df0b9 100644
--- a/car-lib/src/android/car/app/CarSystemUIProxy.java
+++ b/car-lib/src/android/car/app/CarSystemUIProxy.java
@@ -30,10 +30,24 @@
/**
* Creates the host side of the task view and links the provided {@code carTaskViewClient}
* to the same.
+ * This method will be deprecated in Android 15. Please use
+ * {@link CarSystemUIProxy#createCarTaskView(CarTaskViewClient)} instead.
* @return a handle to the host side of task view.
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
@NonNull
CarTaskViewHost createControlledCarTaskView(@NonNull CarTaskViewClient carTaskViewClient);
+
+ /**
+ * Creates the host side of the task view and links the provided {@code
+ * carTaskViewClient} to the same.
+ * @return a handle to the host side of task view.
+ *
+ * @hide
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ CarTaskViewHost createCarTaskView(@NonNull CarTaskViewClient carTaskViewClient);
}
diff --git a/car-lib/src/android/car/app/CarSystemUIProxyAidlWrapper.java b/car-lib/src/android/car/app/CarSystemUIProxyAidlWrapper.java
index 6565b23..31b0245 100644
--- a/car-lib/src/android/car/app/CarSystemUIProxyAidlWrapper.java
+++ b/car-lib/src/android/car/app/CarSystemUIProxyAidlWrapper.java
@@ -32,8 +32,13 @@
@Override
public ICarTaskViewHost createControlledCarTaskView(ICarTaskViewClient client) {
+ return createCarTaskView(client);
+ }
+
+ @Override
+ public ICarTaskViewHost createCarTaskView(ICarTaskViewClient client) {
CarTaskViewHost carTaskViewHost =
- mCarSystemUIProxy.createControlledCarTaskView(new CarTaskViewClient(client));
+ mCarSystemUIProxy.createCarTaskView(new CarTaskViewClient(client));
IBinder.DeathRecipient clientDeathRecipient = new IBinder.DeathRecipient() {
@Override
diff --git a/car-lib/src/android/car/app/CarTaskViewController.java b/car-lib/src/android/car/app/CarTaskViewController.java
index df91150..5cfface 100644
--- a/car-lib/src/android/car/app/CarTaskViewController.java
+++ b/car-lib/src/android/car/app/CarTaskViewController.java
@@ -22,22 +22,22 @@
import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
import android.Manifest;
+import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresApi;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.UiContext;
import android.app.Activity;
+import android.car.Car;
import android.car.annotation.ApiRequirements;
-import android.car.builtin.app.ActivityManagerHelper;
import android.car.builtin.util.Slogf;
+import android.content.Context;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserManager;
-import android.util.Dumpable;
import android.util.Log;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -54,23 +54,28 @@
static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
private final ICarSystemUIProxy mService;
- private final Activity mHostActivity;
- private final List<ControlledRemoteCarTaskView> mControlledRemoteCarTaskViews =
+ private final Context mHostContext;
+ private final CarTaskViewControllerHostLifecycle mLifecycle;
+ private final List<RemoteCarTaskView> mRemoteCarTaskViews =
new ArrayList<>();
private final CarTaskViewInputInterceptor mTaskViewInputInterceptor;
+ private final ICarActivityService mCarActivityService;
+
private boolean mReleased = false;
/**
* @param service the binder interface to communicate with the car system UI.
- * @param hostActivity the activity that will be hosting the taskviews.
* @hide
*/
- CarTaskViewController(@NonNull ICarSystemUIProxy service, @NonNull Activity hostActivity) {
+ CarTaskViewController(@UiContext Context hostContext,
+ @NonNull CarTaskViewControllerHostLifecycle lifecycle,
+ @NonNull ICarSystemUIProxy service,
+ ICarActivityService carActivityService) {
+ mHostContext = hostContext;
mService = service;
- mHostActivity = hostActivity;
-
- mHostActivity.addDumpable(mDumper);
- mTaskViewInputInterceptor = new CarTaskViewInputInterceptor(hostActivity, this);
+ mLifecycle = lifecycle;
+ mCarActivityService = carActivityService;
+ mTaskViewInputInterceptor = new CarTaskViewInputInterceptor(hostContext, lifecycle, this);
}
/**
@@ -79,13 +84,14 @@
* @param callbackExecutor the executor to get the {@link ControlledRemoteCarTaskViewCallback}
* on.
* @param controlledRemoteCarTaskViewCallback the callback to monitor the
- * {@link ControlledRemoteCarTaskView} related
- * events.
+ * {@link ControlledRemoteCarTaskView} related
+ * events.
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
@RequiresPermission(allOf = {Manifest.permission.INJECT_EVENTS,
Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional = true)
+ @MainThread
public void createControlledRemoteCarTaskView(
@NonNull ControlledRemoteCarTaskViewConfig controlledRemoteCarTaskViewConfig,
@NonNull Executor callbackExecutor,
@@ -96,18 +102,18 @@
}
ControlledRemoteCarTaskView taskViewClient =
new ControlledRemoteCarTaskView(
- mHostActivity,
+ mHostContext,
controlledRemoteCarTaskViewConfig,
callbackExecutor,
controlledRemoteCarTaskViewCallback,
/* carTaskViewController= */ this,
- mHostActivity.getSystemService(UserManager.class));
+ mHostContext.getSystemService(UserManager.class));
try {
ICarTaskViewHost host = mService.createControlledCarTaskView(
taskViewClient.mICarTaskViewClient);
taskViewClient.setRemoteHost(host);
- mControlledRemoteCarTaskViews.add(taskViewClient);
+ mRemoteCarTaskViews.add(taskViewClient);
if (controlledRemoteCarTaskViewConfig.mShouldCaptureGestures
|| controlledRemoteCarTaskViewConfig.mShouldCaptureLongPress) {
@@ -120,21 +126,101 @@
}
}
- void onControlledRemoteCarTaskViewReleased(@NonNull ControlledRemoteCarTaskView taskView) {
+ /**
+ * Creates a new {@link RemoteCarRootTaskView}.
+ *
+ * @param callbackExecutor the executor to get the {@link RemoteCarRootTaskViewCallback} on.
+ * @param remoteCarRootTaskViewCallback the callback to monitor the
+ * {@link RemoteCarRootTaskView} related events.
+ * @hide
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
+ @MainThread
+ public void createRemoteCarRootTaskView(
+ @NonNull RemoteCarRootTaskViewConfig remoteCarRootTaskViewConfig,
+ @NonNull Executor callbackExecutor,
+ @NonNull RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback) {
+ assertPlatformVersionAtLeastU();
+ assertPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH);
+ if (mReleased) {
+ throw new IllegalStateException("CarTaskViewController is already released");
+ }
+ RemoteCarRootTaskView taskViewClient =
+ new RemoteCarRootTaskView(
+ mHostContext,
+ remoteCarRootTaskViewConfig,
+ callbackExecutor,
+ remoteCarRootTaskViewCallback,
+ /* carTaskViewController= */ this,
+ mCarActivityService
+ );
+
+ try {
+ ICarTaskViewHost host = mService.createCarTaskView(taskViewClient.mICarTaskViewClient);
+ taskViewClient.setRemoteHost(host);
+ mRemoteCarTaskViews.add(taskViewClient);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "Unable to create root task view.", e);
+ }
+ }
+
+ /**
+ * Creates a new {@link RemoteCarDefaultRootTaskView}.
+ *
+ * @param callbackExecutor the executor to get the {@link RemoteCarDefaultRootTaskViewCallback}
+ * on.
+ * @param remoteCarDefaultRootTaskViewCallback the callback to monitor the
+ * {@link RemoteCarDefaultRootTaskView} related
+ * events.
+ * @hide
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @MainThread
+ public void createRemoteCarDefaultRootTaskView(
+ @NonNull RemoteCarDefaultRootTaskViewConfig remoteCarDefaultRootTaskViewConfig,
+ @NonNull Executor callbackExecutor,
+ @NonNull RemoteCarDefaultRootTaskViewCallback remoteCarDefaultRootTaskViewCallback) {
+ assertPlatformVersionAtLeastU();
+ if (mReleased) {
+ throw new IllegalStateException("CarTaskViewController is already released");
+ }
+ RemoteCarDefaultRootTaskView taskViewClient =
+ new RemoteCarDefaultRootTaskView(
+ mHostContext,
+ remoteCarDefaultRootTaskViewConfig,
+ callbackExecutor,
+ remoteCarDefaultRootTaskViewCallback,
+ /* carTaskViewController= */ this
+ );
+
+ try {
+ ICarTaskViewHost host = mService.createCarTaskView(
+ taskViewClient.mICarTaskViewClient);
+ taskViewClient.setRemoteHost(host);
+ mRemoteCarTaskViews.add(taskViewClient);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "Unable to create default root task view.", e);
+ }
+ }
+
+ void onRemoteCarTaskViewReleased(@NonNull RemoteCarTaskView taskView) {
if (mReleased) {
Log.w(TAG, "Failed to remove the taskView as the "
+ "CarTaskViewController is already released");
return;
}
- if (!mControlledRemoteCarTaskViews.contains(taskView)) {
+ if (!mRemoteCarTaskViews.contains(taskView)) {
Log.w(TAG, "This taskView has already been removed");
return;
}
- mControlledRemoteCarTaskViews.remove(taskView);
+ mRemoteCarTaskViews.remove(taskView);
}
private void assertPermission(String permission) {
- if (mHostActivity.getApplicationContext().checkCallingOrSelfPermission(permission)
+ if (mHostContext.checkCallingOrSelfPermission(permission)
!= PERMISSION_GRANTED) {
throw new SecurityException("requires " + permission);
}
@@ -150,6 +236,7 @@
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
public void release() {
assertPlatformVersionAtLeastU();
if (mReleased) {
@@ -161,10 +248,11 @@
mReleased = true;
}
+ @MainThread
void releaseTaskViews() {
- Iterator<ControlledRemoteCarTaskView> iterator = mControlledRemoteCarTaskViews.iterator();
+ Iterator<RemoteCarTaskView> iterator = mRemoteCarTaskViews.iterator();
while (iterator.hasNext()) {
- ControlledRemoteCarTaskView taskView = iterator.next();
+ RemoteCarTaskView taskView = iterator.next();
// Remove the task view here itself because release triggers removal again which can
// result in concurrent modification exception.
iterator.remove();
@@ -177,12 +265,14 @@
*/
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
public void showEmbeddedTasks() {
assertPlatformVersionAtLeastU();
if (mReleased) {
throw new IllegalStateException("CarTaskViewController is already released");
}
- for (RemoteCarTaskView carTaskView : mControlledRemoteCarTaskViews) {
+ for (int i = 0, length = mRemoteCarTaskViews.size(); i < length; i++) {
+ RemoteCarTaskView carTaskView = mRemoteCarTaskViews.get(i);
// TODO(b/267314188): Add a new method in ICarSystemUI to call
// showEmbeddedTask in a single WCT for multiple tasks.
carTaskView.showEmbeddedTask();
@@ -190,28 +280,10 @@
}
boolean isHostVisible() {
- return ActivityManagerHelper.isVisible(mHostActivity);
+ return mLifecycle.isVisible();
}
- List<ControlledRemoteCarTaskView> getControlledRemoteCarTaskViews() {
- return mControlledRemoteCarTaskViews;
+ List<RemoteCarTaskView> getRemoteCarTaskViews() {
+ return mRemoteCarTaskViews;
}
-
- private final Dumpable mDumper = new Dumpable() {
- private static final String INDENTATION = " ";
-
- @NonNull
- @Override
- public String getDumpableName() {
- return TAG;
- }
-
- @Override
- public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
- writer.println("ControlledRemoteCarTaskViews: ");
- for (ControlledRemoteCarTaskView taskView : mControlledRemoteCarTaskViews) {
- writer.println(INDENTATION + taskView.toString(/* withBounds= */ true));
- }
- }
- };
}
diff --git a/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycle.java b/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycle.java
new file mode 100644
index 0000000..1f42428
--- /dev/null
+++ b/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycle.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.car.annotation.ApiRequirements;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This class represents a handle to the lifecycle of the host (container) that creates the
+ * {@link CarTaskViewController}.
+ * The container can be an activity, fragment, view or a window.
+ *
+ * @hide
+ */
+public final class CarTaskViewControllerHostLifecycle {
+ /** An interface for observing the lifecycle of the container (host). */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public interface CarTaskViewControllerHostLifecycleObserver {
+ /** Gets called when the container is destroyed. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ void onHostDestroyed(CarTaskViewControllerHostLifecycle lifecycle);
+
+ /** Gets called when the container has appeared. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ void onHostAppeared(CarTaskViewControllerHostLifecycle lifecycle);
+
+ /** Gets called when the container has disappeared. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ void onHostDisappeared(CarTaskViewControllerHostLifecycle lifecycle);
+ }
+
+ private final List<CarTaskViewControllerHostLifecycleObserver> mObserverList =
+ new ArrayList<>();
+
+ private boolean mIsVisible = false;
+
+ /**
+ * Notifies the lifecycle observers that the host has been destroyed and they can clean their
+ * internal state.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public void hostDestroyed() {
+ for (CarTaskViewControllerHostLifecycleObserver observer : mObserverList) {
+ observer.onHostDestroyed(this);
+ }
+ }
+
+ /** Notifies the lifecycle observers that the host has appeared. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public void hostAppeared() {
+ mIsVisible = true;
+ for (CarTaskViewControllerHostLifecycleObserver observer : mObserverList) {
+ observer.onHostAppeared(this);
+ }
+ }
+
+ /** Notifies the lifecycle observers that the host has disappeared. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public void hostDisappeared() {
+ mIsVisible = false;
+ for (CarTaskViewControllerHostLifecycleObserver observer : mObserverList) {
+ observer.onHostDisappeared(this);
+ }
+ }
+
+ /** @return true if the container is visible, false otherwise. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
+ /** Registers the given observer to listen to lifecycle of the container. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void registerObserver(CarTaskViewControllerHostLifecycleObserver observer) {
+ mObserverList.add(observer);
+ }
+
+ /** Unregisters the given observer to stop listening to the lifecycle of the container. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void unregisterObserver(CarTaskViewControllerHostLifecycleObserver observer) {
+ mObserverList.remove(observer);
+ }
+}
+
+
diff --git a/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycleFactory.java b/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycleFactory.java
new file mode 100644
index 0000000..c9288fa
--- /dev/null
+++ b/car-lib/src/android/car/app/CarTaskViewControllerHostLifecycleFactory.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.Application;
+import android.car.annotation.ApiRequirements;
+import android.car.builtin.app.ActivityManagerHelper;
+import android.os.Bundle;
+
+/**
+ * A factory to create instances of the {@link CarTaskViewControllerHostLifecycle}.
+ *
+ * @hide
+ */
+public final class CarTaskViewControllerHostLifecycleFactory {
+ private CarTaskViewControllerHostLifecycleFactory() {
+ }
+
+ /**
+ * Creates an instance of {@link CarTaskViewControllerHostLifecycle} which adapts to the
+ * activity lifecycle.
+ *
+ * @param activity the activity which the {@link CarTaskViewControllerHostLifecycle} needs to
+ * be created for.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public static CarTaskViewControllerHostLifecycle forActivity(@NonNull Activity activity) {
+ return new CarTaskViewControllerHostActivityLifecycleAdapter(activity).getLifecycle();
+ }
+
+ private static class CarTaskViewControllerHostActivityLifecycleAdapter
+ implements Application.ActivityLifecycleCallbacks {
+
+ CarTaskViewControllerHostLifecycle mCarTaskViewControllerHostLifecycle;
+
+ CarTaskViewControllerHostActivityLifecycleAdapter(Activity activity) {
+ mCarTaskViewControllerHostLifecycle = new CarTaskViewControllerHostLifecycle();
+ activity.registerActivityLifecycleCallbacks(this);
+ // If the activity is already in resumed state, trigger the host appeared callback
+ // so that the visibility information is latest.
+ if (ActivityManagerHelper.isVisible(activity)) {
+ mCarTaskViewControllerHostLifecycle.hostAppeared();
+ }
+ }
+
+ @Override
+ public void onActivityCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(@NonNull Activity activity) {
+ // Don't invoke hostAppeared() in onStart(), which breaks the CTS
+ // ActivityLifecycleTests#testFinishBelowDialogActivity.
+ }
+
+ @Override
+ public void onActivityResumed(@NonNull Activity activity) {
+ mCarTaskViewControllerHostLifecycle.hostAppeared();
+ }
+
+ @Override
+ public void onActivityPaused(@NonNull Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(@NonNull Activity activity) {
+ mCarTaskViewControllerHostLifecycle.hostDisappeared();
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(@NonNull Activity activity) {
+ mCarTaskViewControllerHostLifecycle.hostDestroyed();
+ }
+
+ public CarTaskViewControllerHostLifecycle getLifecycle() {
+ return mCarTaskViewControllerHostLifecycle;
+ }
+ }
+}
diff --git a/car-lib/src/android/car/app/CarTaskViewControllerSupervisor.java b/car-lib/src/android/car/app/CarTaskViewControllerSupervisor.java
index 39c205e..e9688c6 100644
--- a/car-lib/src/android/car/app/CarTaskViewControllerSupervisor.java
+++ b/car-lib/src/android/car/app/CarTaskViewControllerSupervisor.java
@@ -15,21 +15,27 @@
*/
package android.car.app;
+import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
+
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresApi;
import android.app.Activity;
-import android.app.Application;
+import android.car.app.CarTaskViewControllerHostLifecycle.CarTaskViewControllerHostLifecycleObserver;
import android.car.builtin.app.ActivityManagerHelper;
import android.car.builtin.util.Slogf;
+import android.car.user.CarUserManager;
+import android.car.user.CarUserManager.UserLifecycleListener;
+import android.car.user.UserLifecycleEventFilter;
+import android.content.Context;
import android.os.Build;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
-import java.util.Map;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.concurrent.Executor;
/**
@@ -40,9 +46,10 @@
* clients.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-final class CarTaskViewControllerSupervisor implements Application.ActivityLifecycleCallbacks{
+final class CarTaskViewControllerSupervisor {
private static final String TAG = CarTaskViewControllerSupervisor.class.getSimpleName();
- private final Map<IBinder, ActivityHolder> mActivityHolders = new ArrayMap<>();
+ private final ArrayMap<CarTaskViewControllerHostLifecycle, ActivityHolder> mActivityHolders =
+ new ArrayMap<>();
private final ICarActivityService mCarActivityService;
private final Executor mMainExecutor;
@@ -56,12 +63,50 @@
}
};
+ private final CarTaskViewControllerHostLifecycleObserver
+ mCarTaskViewControllerHostLifecycleObserver =
+ new CarTaskViewControllerHostLifecycleObserver() {
+ public void onHostAppeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ mActivityHolders.get(lifecycle).maybeShowEmbeddedTasks();
+ }
+
+ @Override
+ public void onHostDisappeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ }
+
+ @Override
+ public void onHostDestroyed(CarTaskViewControllerHostLifecycle lifecycle) {
+ lifecycle.unregisterObserver(this);
+
+ ActivityHolder activityHolder = mActivityHolders.remove(lifecycle);
+ activityHolder.onActivityDestroyed();
+
+ // When all the underlying activities are destroyed, the callback should be
+ // removed from the CarActivityService as it's no longer required.
+ // A new callback will be registered when a new activity calls the
+ // createTaskViewController.
+ if (mActivityHolders.isEmpty()) {
+ try {
+ mCarActivityService.removeCarSystemUIProxyCallback(
+ mSystemUIProxyCallback);
+ mSystemUIProxyCallback = null;
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "Failed to remove CarSystemUIProxyCallback", e);
+ }
+ }
+ }
+ };
+
/**
* @param carActivityService the handle to the {@link com.android.car.am.CarActivityService}.
*/
- CarTaskViewControllerSupervisor(ICarActivityService carActivityService, Executor mainExecutor) {
+ CarTaskViewControllerSupervisor(ICarActivityService carActivityService, Executor mainExecutor,
+ @NonNull CarUserManager carUserManager) {
mCarActivityService = carActivityService;
mMainExecutor = mainExecutor;
+ UserLifecycleEventFilter filter = new UserLifecycleEventFilter.Builder()
+ .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED).build();
+ carUserManager.addListener(mainExecutor, filter, mUserLifecycleListener);
}
private static IBinder getToken(Activity activity) {
@@ -82,17 +127,19 @@
*/
@MainThread
void createCarTaskViewController(
+ Context context,
+ @NonNull CarTaskViewControllerHostLifecycle hostActivity,
@NonNull Executor callbackExecutor,
- @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback,
- @NonNull Activity hostActivity) throws RemoteException {
- if (mActivityHolders.containsKey(getToken(hostActivity))) {
+ @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback)
+ throws RemoteException {
+ if (mActivityHolders.containsKey(hostActivity)) {
throw new IllegalArgumentException("A CarTaskViewController already exists for this "
+ "activity. Cannot create another one.");
}
- hostActivity.registerActivityLifecycleCallbacks(this);
- ActivityHolder activityHolder = new ActivityHolder(hostActivity, callbackExecutor,
- carTaskViewControllerCallback);
- mActivityHolders.put(getToken(hostActivity), activityHolder);
+ hostActivity.registerObserver(mCarTaskViewControllerHostLifecycleObserver);
+ ActivityHolder activityHolder = new ActivityHolder(context, hostActivity, callbackExecutor,
+ carTaskViewControllerCallback, mCarActivityService);
+ mActivityHolders.put(hostActivity, activityHolder);
if (mSystemUIProxyCallback != null && mICarSystemUI != null) {
// If there is already a connection with the CarSystemUIProxy, trigger onConnected
@@ -150,90 +197,80 @@
// taskviews again, when system ui will be connected again.
}
- @Override
- public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
- }
-
- @Override
- public void onActivityStarted(@NonNull Activity activity) {}
-
- @Override
- public void onActivityResumed(@NonNull Activity activity) {
- mActivityHolders.get(getToken(activity)).showEmbeddedTasks();
- }
-
- @Override
- public void onActivityPaused(@NonNull Activity activity) {}
-
- @Override
- public void onActivityStopped(@NonNull Activity activity) {}
-
- @Override
- public void onActivitySaveInstanceState(@NonNull Activity activity,
- @NonNull Bundle outState) {}
-
- @Override
- public void onActivityDestroyed(@NonNull Activity activity) {
- activity.unregisterActivityLifecycleCallbacks(this);
-
- ActivityHolder activityHolder = mActivityHolders.remove(getToken(activity));
- activityHolder.onActivityDestroyed();
-
- // When all the underlying activities are destroyed, the callback should be removed
- // from the CarActivityService as its no longer required.
- // A new callback will be registered when a new activity calls the createTaskViewController.
- if (mActivityHolders.isEmpty()) {
- try {
- mCarActivityService.removeCarSystemUIProxyCallback(mSystemUIProxyCallback);
- mSystemUIProxyCallback = null;
- } catch (RemoteException e) {
- Slogf.e(TAG, "Failed to remove CarSystemUIProxyCallback", e);
- }
- }
- }
-
private static final class ActivityHolder {
- private final Activity mActivity;
+ private final Context mContext;
+ private final CarTaskViewControllerHostLifecycle mActivity;
private final Executor mCallbackExecutor;
private final CarTaskViewControllerCallback mCarTaskViewControllerCallback;
+ private final ICarActivityService mCarActivityService;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private CarTaskViewController mCarTaskViewController;
- private ActivityHolder(Activity activity,
+ private ActivityHolder(Context context,
+ CarTaskViewControllerHostLifecycle activity,
Executor callbackExecutor,
- CarTaskViewControllerCallback carTaskViewControllerCallback) {
+ CarTaskViewControllerCallback carTaskViewControllerCallback,
+ ICarActivityService carActivityService) {
+ mContext = context;
mActivity = activity;
mCallbackExecutor = callbackExecutor;
mCarTaskViewControllerCallback = carTaskViewControllerCallback;
+ mCarActivityService = carActivityService;
}
- private void showEmbeddedTasks() {
- if (mCarTaskViewController == null) {
- return;
+ private void maybeShowEmbeddedTasks() {
+ synchronized (mLock) {
+ if (mCarTaskViewController == null || !mCarTaskViewController.isHostVisible()) {
+ return;
+ }
+ mCarTaskViewController.showEmbeddedTasks();
}
- mCarTaskViewController.showEmbeddedTasks();
}
private void onCarSystemUIConnected(ICarSystemUIProxy systemUIProxy) {
- CarTaskViewController taskViewManager =
- new CarTaskViewController(systemUIProxy, mActivity);
- mCarTaskViewController = taskViewManager;
- mCallbackExecutor.execute(() ->
- mCarTaskViewControllerCallback.onConnected(taskViewManager)
- );
+ synchronized (mLock) {
+ mCarTaskViewController =
+ new CarTaskViewController(mContext, mActivity, systemUIProxy,
+ mCarActivityService);
+ }
+ mCallbackExecutor.execute(() -> {
+ synchronized (mLock) {
+ Slogf.w(TAG, "car task view controller not found when triggering callback, "
+ + "not dispatching onConnected");
+ // Check for null because the mCarTaskViewController might have already been
+ // released but this code path is executed later because the executor was busy.
+ if (mCarTaskViewController == null) {
+ return;
+ }
+ mCarTaskViewControllerCallback.onConnected(mCarTaskViewController);
+ }
+ });
}
private void onCarSystemUIDisconnected() {
- if (mCarTaskViewController == null) {
- Slogf.w(TAG, "car task view controller not found, not dispatching onDisconnected");
- return;
+ synchronized (mLock) {
+ if (mCarTaskViewController == null) {
+ Slogf.w(TAG,
+ "car task view controller not found, not dispatching onDisconnected");
+ return;
+ }
+ // Only release the taskviews and not the controller because the system ui might get
+ // connected while the activity is still visible.
+ mCarTaskViewController.releaseTaskViews();
}
- mCallbackExecutor.execute(() ->
- mCarTaskViewControllerCallback.onDisconnected(mCarTaskViewController)
- );
- // Only release the taskviews and not the controller because the system ui might get
- // connected while the activity is still visible.
- mCarTaskViewController.releaseTaskViews();
+
+ mCallbackExecutor.execute(() -> {
+ synchronized (mLock) {
+ if (mCarTaskViewController == null) {
+ Slogf.w(TAG, "car task view controller not found when triggering "
+ + "callback, not dispatching onDisconnected");
+ return;
+ }
+ mCarTaskViewControllerCallback.onDisconnected(mCarTaskViewController);
+ }
+ });
}
private void onActivityDestroyed() {
@@ -241,12 +278,25 @@
}
private void releaseController() {
- if (mCarTaskViewController == null) {
- Slogf.w(TAG, "car task view controller not found, not dispatching onDisconnected");
- return;
+ synchronized (mLock) {
+ if (mCarTaskViewController == null) {
+ Slogf.w(TAG, "car task view controller not found, not releasing");
+ return;
+ }
+ mCarTaskViewController.release();
+ mCarTaskViewController = null;
}
- mCarTaskViewController.release();
- mCarTaskViewController = null;
}
}
+
+ private UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
+ @Override
+ public void onEvent(@NonNull CarUserManager.UserLifecycleEvent event) {
+ // Only called when USER_LIFECYCLE_EVENT_TYPE_UNLOCKED.
+ for (int i = mActivityHolders.size() - 1; i >= 0; --i) {
+ ActivityHolder activityHolder = mActivityHolders.valueAt(i);
+ activityHolder.maybeShowEmbeddedTasks();
+ }
+ }
+ };
}
diff --git a/car-lib/src/android/car/app/CarTaskViewHost.java b/car-lib/src/android/car/app/CarTaskViewHost.java
index 5767b41..8b818de 100644
--- a/car-lib/src/android/car/app/CarTaskViewHost.java
+++ b/car-lib/src/android/car/app/CarTaskViewHost.java
@@ -50,6 +50,25 @@
@Nullable Rect launchBounds);
/**
+ * Creates the root task, which will be embedded inside this task view.
+ *
+ * @hide
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ void createRootTask(int displayId);
+
+ /**
+ * Creates the launch root task, which will be embedded inside this task view.
+ *
+ * @hide
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ void createLaunchRootTask(int displayId, boolean embedHomeTask,
+ boolean embedRecentsTask, boolean embedAssistantTask);
+
+ /**
* Notifies the host side that the client surface has been created.
*
* @param control the {@link SurfaceControl} of the surface that has been created.
diff --git a/car-lib/src/android/car/app/CarTaskViewHostAidlToImplAdapter.java b/car-lib/src/android/car/app/CarTaskViewHostAidlToImplAdapter.java
index 74c8307..a9d3cec 100644
--- a/car-lib/src/android/car/app/CarTaskViewHostAidlToImplAdapter.java
+++ b/car-lib/src/android/car/app/CarTaskViewHostAidlToImplAdapter.java
@@ -45,6 +45,18 @@
}
@Override
+ public void createRootTask(int displayId) {
+ mCarTaskViewHost.createRootTask(displayId);
+ }
+
+ @Override
+ public void createLaunchRootTask(int displayId, boolean embedHomeTask, boolean embedRecentsTask,
+ boolean embedAssistantTask) {
+ mCarTaskViewHost.createLaunchRootTask(displayId, embedHomeTask, embedRecentsTask,
+ embedAssistantTask);
+ }
+
+ @Override
public void notifySurfaceCreated(SurfaceControl control) {
mCarTaskViewHost.notifySurfaceCreated(control);
}
diff --git a/car-lib/src/android/car/app/CarTaskViewInputInterceptor.java b/car-lib/src/android/car/app/CarTaskViewInputInterceptor.java
index 5b283c3..7ce1a91 100644
--- a/car-lib/src/android/car/app/CarTaskViewInputInterceptor.java
+++ b/car-lib/src/android/car/app/CarTaskViewInputInterceptor.java
@@ -22,10 +22,8 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresApi;
-import android.app.Activity;
-import android.app.Application;
+import android.car.app.CarTaskViewControllerHostLifecycle.CarTaskViewControllerHostLifecycleObserver;
import android.car.builtin.input.InputManagerHelper;
import android.car.builtin.view.ViewHelper;
import android.car.builtin.window.WindowManagerHelper;
@@ -34,7 +32,6 @@
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Build;
-import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -64,23 +61,46 @@
private static final Rect sTmpBounds = new Rect();
- private final Activity mHostActivity;
+ private final Context mContext;
+ private final CarTaskViewControllerHostLifecycle mLifecycle;
private final InputManager mInputManager;
private final WindowManager mWm;
private final GestureDetector mGestureDetector =
new GestureDetector(new TaskViewGestureListener());
- private final Application.ActivityLifecycleCallbacks mActivityLifecycleCallbacks =
- new ActivityLifecycleHandler();
+ private final CarTaskViewControllerHostLifecycleObserver mLifeCycleObserver =
+ new CarTaskViewControllerHostLifecycleObserver() {
+ @Override
+ public void onHostDestroyed(CarTaskViewControllerHostLifecycle lifecycle) {
+ }
+
+ @Override
+ public void onHostAppeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ if (!mInitialized) {
+ return;
+ }
+ startInterceptingGestures();
+ }
+
+ @Override
+ public void onHostDisappeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ if (!mInitialized) {
+ return;
+ }
+ stopInterceptingGestures();
+ }
+ };
private final CarTaskViewController mTaskViewController;
private View mSpyWindow;
private boolean mInitialized = false;
- CarTaskViewInputInterceptor(Activity hostActivity, CarTaskViewController taskViewController) {
- mHostActivity = hostActivity;
- mInputManager = hostActivity.getSystemService(InputManager.class);
+ CarTaskViewInputInterceptor(Context context, CarTaskViewControllerHostLifecycle lifecycle,
+ CarTaskViewController taskViewController) {
+ mContext = context;
+ mLifecycle = lifecycle;
+ mInputManager = mContext.getSystemService(InputManager.class);
mTaskViewController = taskViewController;
- mWm = mHostActivity.getSystemService(WindowManager.class);
+ mWm = mContext.getSystemService(WindowManager.class);
}
private static boolean isIn(MotionEvent event, RemoteCarTaskView taskView) {
@@ -88,7 +108,9 @@
return sTmpBounds.contains((int) event.getX(), (int) event.getY());
}
- /** Initializes & starts intercepting gestures. Does nothing if already initialized. */
+ /**
+ * Initializes & starts intercepting gestures. Does nothing if already initialized.
+ */
@MainThread
void init() {
if (mInitialized) {
@@ -96,7 +118,7 @@
return;
}
mInitialized = true;
- mHostActivity.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
+ mLifecycle.registerObserver(mLifeCycleObserver);
startInterceptingGestures();
}
@@ -111,7 +133,7 @@
return;
}
mInitialized = false;
- mHostActivity.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
+ mLifecycle.unregisterObserver(mLifeCycleObserver);
stopInterceptingGestures();
}
@@ -138,7 +160,7 @@
}
private void createAndAddSpyWindow() {
- mSpyWindow = new GestureSpyView(mHostActivity);
+ mSpyWindow = new GestureSpyView(mContext);
WindowManager.LayoutParams p =
new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -166,8 +188,9 @@
}
private final class GestureSpyView extends View {
+
private boolean mConsumingCurrentEventStream = false;
- private boolean mActionDownInsideTaskView = false;
+ private RemoteCarTaskView mActionDownInsideTaskView = null;
private float mTouchDownX;
private float mTouchDownY;
@@ -181,15 +204,17 @@
mGestureDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mActionDownInsideTaskView = false;
+ mActionDownInsideTaskView = null;
- List<ControlledRemoteCarTaskView> taskViewList =
- mTaskViewController.getControlledRemoteCarTaskViews();
- for (ControlledRemoteCarTaskView tv : taskViewList) {
- if (tv.getConfig().mShouldCaptureGestures && isIn(event, tv)) {
+ List<RemoteCarTaskView> taskViewList = mTaskViewController.getRemoteCarTaskViews();
+ for (int i = 0, length = taskViewList.size(); i < length; i++) {
+ RemoteCarTaskView tv = taskViewList.get(i);
+ if (tv instanceof ControlledRemoteCarTaskView
+ && ((ControlledRemoteCarTaskView) tv).getConfig()
+ .mShouldCaptureGestures && isIn(event, tv)) {
mTouchDownX = event.getX();
mTouchDownY = event.getY();
- mActionDownInsideTaskView = true;
+ mActionDownInsideTaskView = tv;
break;
}
}
@@ -199,7 +224,7 @@
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
- if (!mConsumingCurrentEventStream && mActionDownInsideTaskView
+ if (!mConsumingCurrentEventStream && mActionDownInsideTaskView != null
&& Float.compare(mTouchDownX, event.getX()) != 0
&& Float.compare(mTouchDownY, event.getY()) != 0) {
// Start consuming on ACTION_MOVE when ACTION_DOWN happened inside TaskView
@@ -217,11 +242,10 @@
// is meant to be the first event in an event stream.
MotionEvent cloneEvent = MotionEvent.obtain(event);
cloneEvent.setAction(MotionEvent.ACTION_DOWN);
- CarTaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(
- cloneEvent);
+ dispatchEvent(mActionDownInsideTaskView, cloneEvent);
cloneEvent.recycle();
}
- CarTaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(event);
+ dispatchEvent(mActionDownInsideTaskView, event);
}
}
@@ -230,21 +254,30 @@
if (mConsumingCurrentEventStream) {
// Disable the propagation when handling manually.
InputManagerHelper.pilferPointers(mInputManager, this);
- CarTaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(event);
+ dispatchEvent(mActionDownInsideTaskView, event);
}
mConsumingCurrentEventStream = false;
}
return false;
}
+
+ private static void dispatchEvent(RemoteCarTaskView taskView, MotionEvent event) {
+ if (taskView.getRootView() == null) {
+ return;
+ }
+ taskView.getRootView().dispatchTouchEvent(event);
+ }
}
private final class TaskViewGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public void onLongPress(@NonNull MotionEvent e) {
- List<ControlledRemoteCarTaskView> taskViewList =
- mTaskViewController.getControlledRemoteCarTaskViews();
- for (ControlledRemoteCarTaskView tv : taskViewList) {
- if (tv.getConfig().mShouldCaptureLongPress && isIn(e, tv)) {
+ List<RemoteCarTaskView> taskViewList = mTaskViewController.getRemoteCarTaskViews();
+ for (int i = 0, length = taskViewList.size(); i < length; i++) {
+ RemoteCarTaskView tv = taskViewList.get(i);
+ if (tv instanceof ControlledRemoteCarTaskView
+ && ((ControlledRemoteCarTaskView) tv).getConfig().mShouldCaptureGestures
+ && isIn(e, tv)) {
if (DBG) {
Log.d(TAG, "Long press captured for taskView: " + tv);
}
@@ -258,44 +291,4 @@
}
}
}
-
- private final class ActivityLifecycleHandler implements Application.ActivityLifecycleCallbacks {
- @Override
- public void onActivityCreated(
- @NonNull Activity activity, @Nullable Bundle savedInstanceState) {
- }
-
- @Override
- public void onActivityStarted(@NonNull Activity activity) {
- if (!mInitialized) {
- return;
- }
- startInterceptingGestures();
- }
-
- @Override
- public void onActivityResumed(@NonNull Activity activity) {
- }
-
- @Override
- public void onActivityPaused(@NonNull Activity activity) {
- }
-
- @Override
- public void onActivityStopped(@NonNull Activity activity) {
- if (!mInitialized) {
- return;
- }
- stopInterceptingGestures();
- }
-
- @Override
- public void onActivitySaveInstanceState(
- @NonNull Activity activity, @NonNull Bundle outState) {
- }
-
- @Override
- public void onActivityDestroyed(@NonNull Activity activity) {
- }
- }
}
diff --git a/car-lib/src/android/car/app/ControlledRemoteCarTaskView.java b/car-lib/src/android/car/app/ControlledRemoteCarTaskView.java
index fa0f225..01838da 100644
--- a/car-lib/src/android/car/app/ControlledRemoteCarTaskView.java
+++ b/car-lib/src/android/car/app/ControlledRemoteCarTaskView.java
@@ -20,6 +20,7 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresApi;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -34,6 +35,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
+import android.graphics.Region;
import android.os.UserManager;
import android.view.Display;
import android.view.SurfaceControl;
@@ -47,7 +49,7 @@
* The underlying task will be restarted if it is crashed depending on the
* {@code autoRestartOnCrash}.
*
- * It should be preferred when:
+ * <p>It should be preferred when:
* <ul>
* <li>The underlying task is meant to be started by the host and be there forever.</li>
* </ul>
@@ -67,6 +69,49 @@
private final ControlledRemoteCarTaskViewConfig mConfig;
private final Rect mTmpRect = new Rect();
+ private ActivityManager.RunningTaskInfo mTaskInfo;
+
+ final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ mTaskInfo = taskInfo;
+ updateWindowBounds();
+ if (taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ ControlledRemoteCarTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ ControlledRemoteCarTaskView.this.onTaskAppeared(taskInfo, leash);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ ControlledRemoteCarTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ ControlledRemoteCarTaskView.this.onTaskInfoChanged(taskInfo);
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskInfo = null;
+ ControlledRemoteCarTaskView.this.onTaskVanished(taskInfo);
+ }
+
+ @Override
+ public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
+ ViewHelper.seResizeBackgroundColor(ControlledRemoteCarTaskView.this, color);
+ }
+
+ @Override
+ public Rect getCurrentBoundsOnScreen() {
+ ViewHelper.getBoundsOnScreen(ControlledRemoteCarTaskView.this, mTmpRect);
+ return mTmpRect;
+ }
+ };
+
ControlledRemoteCarTaskView(
@NonNull Context context,
ControlledRemoteCarTaskViewConfig config,
@@ -146,8 +191,9 @@
@Override
void onReleased() {
+ mTaskInfo = null;
mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased());
- mCarTaskViewController.onControlledRemoteCarTaskViewReleased(this);
+ mCarTaskViewController.onRemoteCarTaskViewReleased(this);
}
@Override
@@ -192,6 +238,19 @@
mCallbackExecutor.execute(() -> mCallback.onTaskVanished(taskInfo));
}
+ /**
+ * @return the {@link android.app.ActivityManager.RunningTaskInfo} of the task currently
+ * running in the TaskView.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ @Override
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
ControlledRemoteCarTaskViewConfig getConfig() {
return mConfig;
}
@@ -210,6 +269,79 @@
+ " taskId=" + (getTaskInfo() == null ? "null" : getTaskInfo().taskId) + "\n"
+ (withBounds ? (" boundsOnScreen=" + mTmpRect) : "")
+ "}\n";
+ }
+ // Since SurfaceView is public, these methods need to be overridden. Details in b/296680464.
+ @Override
+ @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public void addInsets(int index, int type, @NonNull Rect frame) {
+ super.addInsets(index, type, frame);
+ }
+
+ @Override
+ @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ public void removeInsets(int index, int type) {
+ super.removeInsets(index, type);
+ }
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public void release() {
+ super.release();
+ }
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public boolean isInitialized() {
+ return super.isInitialized();
+ }
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public void setObscuredTouchRegion(@NonNull Region obscuredRegion) {
+ super.setObscuredTouchRegion(obscuredRegion);
+ }
+
+ @Override
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public void setObscuredTouchRect(@NonNull Rect obscuredRect) {
+ super.setObscuredTouchRect(obscuredRect);
+ }
+
+ @Override
+ @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
+ public void updateWindowBounds() {
+ super.updateWindowBounds();
}
}
diff --git a/car-lib/src/android/car/app/ControlledRemoteCarTaskViewConfig.java b/car-lib/src/android/car/app/ControlledRemoteCarTaskViewConfig.java
index 38e5192..9414360 100644
--- a/car-lib/src/android/car/app/ControlledRemoteCarTaskViewConfig.java
+++ b/car-lib/src/android/car/app/ControlledRemoteCarTaskViewConfig.java
@@ -30,7 +30,7 @@
*/
@SystemApi
public final class ControlledRemoteCarTaskViewConfig {
- private static final String TAG = "ControlledRemoteCarTaskView";
+ private static final String TAG = ControlledRemoteCarTaskViewConfig.class.getSimpleName();
final Intent mActivityIntent;
final boolean mShouldAutoRestartOnTaskRemoval;
diff --git a/car-lib/src/android/car/app/ICarSystemUIProxy.aidl b/car-lib/src/android/car/app/ICarSystemUIProxy.aidl
index 3afe4ec..139c99c 100644
--- a/car-lib/src/android/car/app/ICarSystemUIProxy.aidl
+++ b/car-lib/src/android/car/app/ICarSystemUIProxy.aidl
@@ -29,10 +29,15 @@
*/
interface ICarSystemUIProxy {
/**
+ * @deprecated, use createRootTaskView()
+ */
+ ICarTaskViewHost createControlledCarTaskView(in ICarTaskViewClient client);
+
+ /**
* Creates the host side of the task view and links the provided {@code carTaskVIewClient}
* to the same.
*
* @return a handle to the host side of task view.
*/
- ICarTaskViewHost createControlledCarTaskView(in ICarTaskViewClient client);
-}
\ No newline at end of file
+ ICarTaskViewHost createCarTaskView(in ICarTaskViewClient client);
+}
diff --git a/car-lib/src/android/car/app/ICarTaskViewHost.aidl b/car-lib/src/android/car/app/ICarTaskViewHost.aidl
index ecf7c35..5ef193e 100644
--- a/car-lib/src/android/car/app/ICarTaskViewHost.aidl
+++ b/car-lib/src/android/car/app/ICarTaskViewHost.aidl
@@ -31,6 +31,9 @@
oneway interface ICarTaskViewHost {
void release();
void startActivity(in PendingIntent pendingIntent, in Intent intent, in Bundle options, in Rect launchBounds);
+ void createRootTask(int displayId);
+ void createLaunchRootTask(int displayId, boolean embedHomeTask, boolean embedRecentsTask,
+ boolean embedAssistantTask);
void notifySurfaceCreated(in SurfaceControl control);
void setWindowBounds(in Rect bounds);
void notifySurfaceDestroyed();
diff --git a/car-lib/src/android/car/app/RemoteCarDefaultRootTaskView.java b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskView.java
new file mode 100644
index 0000000..821d2d5
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskView.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresApi;
+import android.app.ActivityManager;
+import android.car.annotation.ApiRequirements;
+import android.car.builtin.view.ViewHelper;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.SurfaceControl;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link RemoteCarDefaultRootTaskView} can act as a default app container. A default app
+ * container is the container where all apps open by default.
+ *
+ * Please use this with caution. If this task view is used inside an activity, that activity might
+ * always remain running as all the other activities will start appearing inside this launch root
+ * instead of appearing in the full screen.
+ *
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+public final class RemoteCarDefaultRootTaskView extends RemoteCarTaskView {
+ private static final String TAG = RemoteCarDefaultRootTaskView.class.getSimpleName();
+
+ private final Executor mCallbackExecutor;
+ private final RemoteCarDefaultRootTaskViewCallback mCallback;
+ private final CarTaskViewController mCarTaskViewController;
+ private final RemoteCarDefaultRootTaskViewConfig mConfig;
+ private final Rect mTmpRect = new Rect();
+ private final RootTaskStackManager mRootTaskStackManager = new RootTaskStackManager();
+
+ private ActivityManager.RunningTaskInfo mRootTask;
+
+ final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mRootTask == null) {
+ mRootTask = taskInfo;
+ // If onTaskAppeared() is called, it implicitly means that super.isInitialized()
+ // is true, as the root task is created only after initialization.
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewInitialized());
+
+ if (taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ RemoteCarDefaultRootTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ updateWindowBounds();
+ }
+
+ mRootTaskStackManager.taskAppeared(taskInfo, leash);
+ mCallbackExecutor.execute(() -> mCallback.onTaskAppeared(taskInfo));
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask.taskId == taskInfo.taskId && taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ RemoteCarDefaultRootTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ mRootTaskStackManager.taskInfoChanged(taskInfo);
+ mCallbackExecutor.execute(() -> mCallback.onTaskInfoChanged(taskInfo));
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask.taskId == taskInfo.taskId) {
+ mRootTask = null;
+ }
+ mRootTaskStackManager.taskVanished(taskInfo);
+ mCallbackExecutor.execute(() -> mCallback.onTaskVanished(taskInfo));
+ }
+
+ @Override
+ public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
+ ViewHelper.seResizeBackgroundColor(RemoteCarDefaultRootTaskView.this, color);
+ }
+
+ @Override
+ public Rect getCurrentBoundsOnScreen() {
+ ViewHelper.getBoundsOnScreen(RemoteCarDefaultRootTaskView.this, mTmpRect);
+ return mTmpRect;
+ }
+ };
+
+ RemoteCarDefaultRootTaskView(
+ @NonNull Context context,
+ RemoteCarDefaultRootTaskViewConfig config,
+ @NonNull Executor callbackExecutor,
+ @NonNull RemoteCarDefaultRootTaskViewCallback callback,
+ CarTaskViewController carTaskViewController) {
+ super(context);
+ mConfig = config;
+ mCallbackExecutor = callbackExecutor;
+ mCallback = callback;
+ mCarTaskViewController = carTaskViewController;
+
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this));
+ }
+
+ /**
+ * Returns the task info of the top task running in the root task embedded in this task view.
+ *
+ * @return task info object of the top task.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTopTaskInfo() {
+ return mRootTaskStackManager.getTopTask();
+ }
+
+ @Override
+ void onInitialized() {
+ // A signal when the surface and host-side are ready. This task view initialization
+ // completes after root task has been created and set as launch root.
+ createLaunchRootTask(mConfig.getDisplayId(), mConfig.embedsHomeTask(),
+ mConfig.embedsRecentsTask(), mConfig.embedsAssistantTask());
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return super.isInitialized() && mRootTask != null;
+ }
+
+ @Override
+ void onReleased() {
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased());
+ mCarTaskViewController.onRemoteCarTaskViewReleased(this);
+ }
+
+ @Nullable
+ @Override
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mRootTask;
+ }
+
+ RemoteCarDefaultRootTaskViewConfig getConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public String toString() {
+ return toString(/* withBounds= */ false);
+ }
+
+ String toString(boolean withBounds) {
+ if (withBounds) {
+ ViewHelper.getBoundsOnScreen(this, mTmpRect);
+ }
+ return TAG + " {\n"
+ + " config=" + mConfig + "\n"
+ + " rootTaskId=" + (getTaskInfo() == null ? "null" : getTaskInfo().taskId) + "\n"
+ + (withBounds ? (" boundsOnScreen=" + mTmpRect) : "")
+ + "}\n";
+
+ }
+}
diff --git a/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewCallback.java b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewCallback.java
new file mode 100644
index 0000000..c687414
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.car.annotation.ApiRequirements;
+
+/**
+ * A callback interface for {@link RemoteCarDefaultRootTaskView}.
+ *
+ * @hide
+ */
+public interface RemoteCarDefaultRootTaskViewCallback
+ extends RemoteCarTaskViewCallback<RemoteCarDefaultRootTaskView> {
+ /**
+ * Called when the underlying {@link RemoteCarDefaultRootTaskView} instance is created.
+ *
+ * @param taskView the new newly created {@link RemoteCarDefaultRootTaskView} instance.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewCreated(@NonNull RemoteCarDefaultRootTaskView taskView) {}
+
+ /**
+ * Called when the underlying {@link RemoteCarDefaultRootTaskView} is ready. A
+ * {@link RemoteCarDefaultRootTaskView} is considered ready when it has completed all the
+ * set up that is required. This callback is only triggered once.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewInitialized() {}
+
+ /**
+ * Called when the underlying {@link RemoteCarDefaultRootTaskView} is released.
+ * This callback is only triggered once.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewReleased() {}
+}
diff --git a/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewConfig.java b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewConfig.java
new file mode 100644
index 0000000..1e48396
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarDefaultRootTaskViewConfig.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
+
+import android.annotation.NonNull;
+import android.car.annotation.ApiRequirements;
+
+/**
+ * This class provides the required configuration to create a {@link RemoteCarRootTaskView}.
+ *
+ * @hide
+ */
+public final class RemoteCarDefaultRootTaskViewConfig {
+ private static final String TAG = RemoteCarDefaultRootTaskViewConfig.class.getSimpleName();
+
+ private final int mDisplayId;
+ private final boolean mEmbedHomeTask;
+ private final boolean mEmbedRecentsTask;
+ private final boolean mEmbedAssistantTask;
+
+ private RemoteCarDefaultRootTaskViewConfig(int displayId, boolean embedHomeTask,
+ boolean embedRecentsTask, boolean embedAssistantTask) {
+ mDisplayId = displayId;
+ mEmbedHomeTask = embedHomeTask;
+ mEmbedRecentsTask = embedRecentsTask;
+ mEmbedAssistantTask = embedAssistantTask;
+ }
+
+ /** See {@link Builder#setDisplayId(int)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** See {@link Builder#embedHomeTask(boolean)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public boolean embedsHomeTask() {
+ return mEmbedHomeTask;
+ }
+
+ /** See {@link Builder#embedRecentsTask(boolean)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public boolean embedsRecentsTask() {
+ return mEmbedRecentsTask;
+ }
+
+ /** See {@link Builder#embedAssistantTask(boolean)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public boolean embedsAssistantTask() {
+ return mEmbedAssistantTask;
+ }
+
+ @Override
+ public String toString() {
+ return TAG + " {"
+ + " displayId=" + mDisplayId
+ + " embedHomeTask=" + mEmbedHomeTask
+ + " embedRecentsTask=" + mEmbedRecentsTask
+ + " embedAssistantTask=" + mEmbedAssistantTask
+ + '}';
+ }
+
+ /**
+ * A builder class for {@link RemoteCarDefaultRootTaskViewConfig}.
+ *
+ * @hide
+ */
+ public static final class Builder {
+ private int mDisplayId;
+ private boolean mEmbedHomeTask;
+ private boolean mEmbedRecentsTask;
+ private boolean mEmbedAssistantTask;
+
+ public Builder() {
+ }
+
+ /** Sets the display Id of the display which the root task will be created for. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder setDisplayId(int displayId) {
+ mDisplayId = displayId;
+ return this;
+ }
+
+ /** Creates the {@link RemoteCarDefaultRootTaskViewConfig} object. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public RemoteCarDefaultRootTaskViewConfig build() {
+ assertPlatformVersionAtLeastU();
+ return new RemoteCarDefaultRootTaskViewConfig(mDisplayId, mEmbedHomeTask,
+ mEmbedRecentsTask, mEmbedAssistantTask);
+ }
+
+ /**
+ * Sets the flag indicating whether the tasks with {@code ACTIVITY_TYPE_HOME} should be
+ * embedded in the root task.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder embedHomeTask(boolean embedHomeTask) {
+ mEmbedHomeTask = embedHomeTask;
+ return this;
+ }
+
+ /**
+ * Sets the flag indicating whether the tasks with {@code ACTIVITY_TYPE_RECENTS} should be
+ * embedded in the root task.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder embedRecentsTask(boolean embedRecentsTask) {
+ mEmbedRecentsTask = embedRecentsTask;
+ return this;
+ }
+
+ /**
+ * Sets the flag indicating whether the tasks with {@code ACTIVITY_TYPE_ASSISTANT}
+ * should be embedded in the root task.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder embedAssistantTask(boolean embedAssistantTask) {
+ mEmbedAssistantTask = embedAssistantTask;
+ return this;
+ }
+ }
+}
diff --git a/car-lib/src/android/car/app/RemoteCarRootTaskView.java b/car-lib/src/android/car/app/RemoteCarRootTaskView.java
new file mode 100644
index 0000000..b0182ba
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarRootTaskView.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresApi;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
+import android.car.Car;
+import android.car.annotation.ApiRequirements;
+import android.car.builtin.app.TaskInfoHelper;
+import android.car.builtin.view.ViewHelper;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link RemoteCarRootTaskView} should be used when a given list of activities are required to
+ * appear inside the bounds of a the given {@link android.view.View} in the form of a task stack.
+ * This {@link RemoteCarTaskView} creates a root task and all the given activities will launch
+ * inside that root task.
+ *
+ * <p>It serves these use-cases:
+ * <ul>
+ * <li>Should be used when the apps that are meant to be in it can be started from anywhere
+ * in the system. i.e. when the host app has no control over their launching.</li>
+ * <li>Suitable for apps like Assistant or Setup-Wizard.</li>
+ * </ul>
+ *
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+public final class RemoteCarRootTaskView extends RemoteCarTaskView {
+ private static final String TAG = RemoteCarRootTaskView.class.getSimpleName();
+
+ private final Executor mCallbackExecutor;
+ private final RemoteCarRootTaskViewCallback mCallback;
+ private final ICarActivityService mCarActivityService;
+ private final CarTaskViewController mCarTaskViewController;
+ private final Rect mTmpRect = new Rect();
+ private final RootTaskStackManager mRootTaskStackManager = new RootTaskStackManager();
+ private final Object mLock = new Object();
+ private final int mDisplayId;
+ /**
+ * List of activities that appear in this {@link RemoteCarRootTaskView}. It's initialized
+ * with the value from {@link RemoteCarRootTaskViewConfig#getAllowListedActivities()} and
+ * can be updated by {@link #updateAllowListedActivities(List)}.
+ */
+ @GuardedBy("mLock")
+ private final ArrayList<ComponentName> mAllowListedActivities;
+
+ private ActivityManager.RunningTaskInfo mRootTask;
+
+ final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mRootTask == null) {
+ mRootTask = taskInfo;
+ synchronized (mLock) {
+ setPersistentActivitiesOnRootTask(mAllowListedActivities,
+ TaskInfoHelper.getToken(taskInfo));
+ }
+
+ // If onTaskAppeared() is called, it implicitly means that super.isInitialized()
+ // is true, as the root task is created only after initialization.
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewInitialized());
+
+ if (taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ RemoteCarRootTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ updateWindowBounds();
+ }
+
+ mRootTaskStackManager.taskAppeared(taskInfo, leash);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask.taskId == taskInfo.taskId && taskInfo.taskDescription != null) {
+ ViewHelper.seResizeBackgroundColor(
+ RemoteCarRootTaskView.this,
+ taskInfo.taskDescription.getBackgroundColor());
+ }
+ mRootTaskStackManager.taskInfoChanged(taskInfo);
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask.taskId == taskInfo.taskId) {
+ mRootTask = null;
+ }
+ mRootTaskStackManager.taskVanished(taskInfo);
+ }
+
+ @Override
+ public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
+ ViewHelper.seResizeBackgroundColor(RemoteCarRootTaskView.this, color);
+ }
+
+ @Override
+ public Rect getCurrentBoundsOnScreen() {
+ ViewHelper.getBoundsOnScreen(RemoteCarRootTaskView.this, mTmpRect);
+ return mTmpRect;
+ }
+ };
+
+ RemoteCarRootTaskView(
+ @NonNull Context context,
+ RemoteCarRootTaskViewConfig config,
+ @NonNull Executor callbackExecutor,
+ @NonNull RemoteCarRootTaskViewCallback callback,
+ CarTaskViewController carTaskViewController,
+ @NonNull ICarActivityService carActivityService) {
+ super(context);
+ mCallbackExecutor = callbackExecutor;
+ mCallback = callback;
+ mCarTaskViewController = carTaskViewController;
+ mCarActivityService = carActivityService;
+ synchronized (mLock) {
+ mAllowListedActivities = new ArrayList<>(config.getAllowListedActivities());
+ }
+ mDisplayId = config.getDisplayId();
+
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this));
+ }
+
+ /**
+ * Returns the task info of the top task running in the root task embedded in this task view.
+ *
+ * @return task info object of the top task.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTopTaskInfo() {
+ return mRootTaskStackManager.getTopTask();
+ }
+
+ @Override
+ void onInitialized() {
+ // A signal when the surface and host-side are ready. This task view initialization
+ // completes after root task has been created.
+ createRootTask(mDisplayId);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return super.isInitialized() && mRootTask != null;
+ }
+
+ @Override
+ void onReleased() {
+ mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased());
+ mCarTaskViewController.onRemoteCarTaskViewReleased(this);
+ }
+
+ @Nullable
+ @Override
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mRootTask;
+ }
+
+ @Override
+ public String toString() {
+ return toString(/* withBounds= */ false);
+ }
+
+ /**
+ * Updates the list of activities that appear inside this {@link RemoteCarRootTaskView}.
+ *
+ * <p>Note:
+ * If an activity is already associated with another {@link RemoteCarRootTaskView}, its
+ * designation will be overridden.
+ *
+ * @param list list of {@link ComponentName} of activities to be designated to this
+ * {@link RemoteCarRootTaskView}
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
+ @MainThread
+ public void updateAllowListedActivities(List<ComponentName> list) {
+ synchronized (mLock) {
+ if (mRootTask != null) {
+ List<ComponentName> activitiesToRemove = findDifferences(mAllowListedActivities,
+ list);
+ List<ComponentName> activitiesToAdd = findDifferences(list, mAllowListedActivities);
+ setPersistentActivitiesOnRootTask(activitiesToRemove, /* launchCookie= */ null);
+ setPersistentActivitiesOnRootTask(activitiesToAdd,
+ TaskInfoHelper.getToken(mRootTask));
+ }
+
+ mAllowListedActivities.clear();
+ mAllowListedActivities.addAll(list);
+ }
+ }
+
+ /**
+ * Returns all the {@link ComponentName}s in {@code firstList} which are not in
+ * {@code secondList}.
+ */
+ private List<ComponentName> findDifferences(List<ComponentName> firstList,
+ List<ComponentName> secondList) {
+ ArrayList<ComponentName> result = new ArrayList<>(firstList);
+ result.removeAll(secondList);
+ return result;
+ }
+
+ private void setPersistentActivitiesOnRootTask(List<ComponentName> activities,
+ IBinder launchCookie) {
+ try {
+ mCarActivityService.setPersistentActivitiesOnRootTask(activities, launchCookie);
+ } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
+ throw e;
+ } catch (ServiceSpecificException e) {
+ throw new IllegalStateException(
+ "Car service looks crashed on ServiceSpecificException " + e);
+ } catch (RemoteException | RuntimeException e) {
+ throw new IllegalStateException("Car service looks crashed on RemoteException " + e);
+ }
+ }
+
+ String toString(boolean withBounds) {
+ if (withBounds) {
+ ViewHelper.getBoundsOnScreen(this, mTmpRect);
+ }
+
+ StringBuilder b = new StringBuilder(TAG).append(" {\n");
+
+ b.append(" mDisplayId=").append(mDisplayId);
+ b.append("\n");
+
+ b.append(" taskId=").append((getTaskInfo() == null ? "null" : getTaskInfo().taskId));
+ b.append("\n");
+
+ if (withBounds) {
+ b.append(" boundsOnScreen=").append(mTmpRect);
+ b.append("\n");
+ }
+
+ b.append(" mAllowListedActivities= [");
+ synchronized (mLock) {
+ for (ComponentName componentName : mAllowListedActivities) {
+ b.append("\n ").append(componentName);
+ }
+ }
+ b.append(" ]}\n");
+
+ return b.toString();
+ }
+}
diff --git a/car-lib/src/android/car/app/RemoteCarRootTaskViewCallback.java b/car-lib/src/android/car/app/RemoteCarRootTaskViewCallback.java
new file mode 100644
index 0000000..7baceb9
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarRootTaskViewCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.car.annotation.ApiRequirements;
+
+/**
+ * A callback interface for {@link ControlledRemoteCarTaskView}.
+ *
+ * @hide
+ */
+public interface RemoteCarRootTaskViewCallback
+ extends RemoteCarTaskViewCallback<RemoteCarRootTaskView> {
+ /**
+ * Called when the underlying {@link RemoteCarTaskView} instance is created.
+ *
+ * @param taskView the new newly created {@link RemoteCarTaskView} instance.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewCreated(@NonNull RemoteCarRootTaskView taskView) {}
+
+ /**
+ * Called when the underlying {@link RemoteCarTaskView} is ready. A {@link RemoteCarTaskView}
+ * is considered ready when it has completed all the set up that is required.
+ * This callback is only triggered once.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewInitialized() {}
+
+ /**
+ * Called when the underlying {@link RemoteCarTaskView} is released.
+ * This callback is only triggered once.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @Override
+ default void onTaskViewReleased() {}
+}
diff --git a/car-lib/src/android/car/app/RemoteCarRootTaskViewConfig.java b/car-lib/src/android/car/app/RemoteCarRootTaskViewConfig.java
new file mode 100644
index 0000000..8e3167e
--- /dev/null
+++ b/car-lib/src/android/car/app/RemoteCarRootTaskViewConfig.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
+
+import android.annotation.NonNull;
+import android.car.annotation.ApiRequirements;
+import android.content.ComponentName;
+
+import java.util.List;
+
+/**
+ * This class provides the required configuration to create a {@link RemoteCarRootTaskView}.
+ *
+ * @hide
+ */
+public final class RemoteCarRootTaskViewConfig {
+ private static final String TAG = RemoteCarRootTaskViewConfig.class.getSimpleName();
+
+ private final int mDisplayId;
+ private final List<ComponentName> mAllowListedActivities;
+
+ private RemoteCarRootTaskViewConfig(int displayId,
+ @NonNull List<ComponentName> allowListedActivities) {
+ mDisplayId = displayId;
+ mAllowListedActivities = allowListedActivities;
+ }
+
+ /** See {@link Builder#setDisplayId(int)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** See {@link Builder#setAllowListedActivities(List)}. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ public List<ComponentName> getAllowListedActivities() {
+ return mAllowListedActivities;
+ }
+
+ @Override
+ public String toString() {
+ return TAG + " {"
+ + " displayId=" + mDisplayId
+ + " allowListedActivities= " + mAllowListedActivities
+ + '}';
+ }
+
+ /**
+ * A builder class for {@link RemoteCarRootTaskViewConfig}.
+ *
+ * @hide
+ */
+ public static final class Builder {
+ private int mDisplayId;
+
+ private List<ComponentName> mAllowListedActivities;
+
+ public Builder() {
+ }
+
+ /** Sets the display Id of the display which the root task will be created for. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder setDisplayId(int displayId) {
+ mDisplayId = displayId;
+ return this;
+ }
+
+ /**
+ * Sets the initial list of all the allow listed activities which will be persisted on the
+ * root task that is embedded inside the task view.
+ */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public Builder setAllowListedActivities(
+ @NonNull List<ComponentName> allowListedActivities) {
+ mAllowListedActivities = allowListedActivities;
+ return this;
+ }
+
+ /** Creates the {@link RemoteCarRootTaskViewConfig} object. */
+ @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
+ minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
+ @NonNull
+ public RemoteCarRootTaskViewConfig build() {
+ assertPlatformVersionAtLeastU();
+ return new RemoteCarRootTaskViewConfig(mDisplayId, mAllowListedActivities);
+ }
+ }
+}
diff --git a/car-lib/src/android/car/app/RemoteCarTaskView.java b/car-lib/src/android/car/app/RemoteCarTaskView.java
index fed43e6..8ffeb26 100644
--- a/car-lib/src/android/car/app/RemoteCarTaskView.java
+++ b/car-lib/src/android/car/app/RemoteCarTaskView.java
@@ -49,60 +49,20 @@
* A {@link SurfaceView} that can embed a Task inside of it. The task management is done remotely
* in a process that has registered a TaskOrganizer with the system server.
* Usually this process is the Car System UI.
+ *
+ * @hide
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-abstract class RemoteCarTaskView extends SurfaceView {
+public abstract class RemoteCarTaskView extends SurfaceView {
private static final String TAG = RemoteCarTaskView.class.getSimpleName();
private final TouchableInsetsProvider mTouchableInsetsProvider;
private final SurfaceCallbackHandler mSurfaceCallbackHandler = new SurfaceCallbackHandler();
private final Rect mTmpRect = new Rect();
- boolean mInitialized = false;
+ private boolean mInitialized = false;
boolean mSurfaceCreated = false;
private Region mObscuredTouchRegion;
private ICarTaskViewHost mICarTaskViewHost;
- private ActivityManager.RunningTaskInfo mTaskInfo;
-
- final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- mTaskInfo = taskInfo;
- updateWindowBounds();
- if (taskInfo.taskDescription != null) {
- ViewHelper.seResizeBackgroundColor(
- RemoteCarTaskView.this,
- taskInfo.taskDescription.getBackgroundColor());
- }
- RemoteCarTaskView.this.onTaskAppeared(taskInfo, leash);
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (taskInfo.taskDescription != null) {
- ViewHelper.seResizeBackgroundColor(
- RemoteCarTaskView.this,
- taskInfo.taskDescription.getBackgroundColor());
- }
- RemoteCarTaskView.this.onTaskInfoChanged(taskInfo);
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- mTaskInfo = null;
- RemoteCarTaskView.this.onTaskVanished(taskInfo);
- }
-
- @Override
- public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
- ViewHelper.seResizeBackgroundColor(RemoteCarTaskView.this, color);
- }
-
- @Override
- public Rect getCurrentBoundsOnScreen() {
- ViewHelper.getBoundsOnScreen(RemoteCarTaskView.this, mTmpRect);
- return mTmpRect;
- }
- };
RemoteCarTaskView(Context context) {
super(context);
@@ -174,9 +134,7 @@
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
@MainThread
- @Nullable public ActivityManager.RunningTaskInfo getTaskInfo() {
- return mTaskInfo;
- }
+ @Nullable public abstract ActivityManager.RunningTaskInfo getTaskInfo();
/**
* @return true, if the task view is initialized.
@@ -207,6 +165,7 @@
@RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
public void addInsets(int index, int type, @NonNull Rect frame) {
try {
mICarTaskViewHost.addInsets(index, type, frame);
@@ -269,14 +228,32 @@
}
}
+ void createRootTask(int displayId) {
+ try {
+ mICarTaskViewHost.createRootTask(displayId);
+ } catch (RemoteException exception) {
+ Slogf.e(TAG, "exception in createRootTask", exception);
+ }
+ }
+
+ void createLaunchRootTask(int displayId, boolean embedHomeTask, boolean embedRecentsTask,
+ boolean embedAssistantTask) {
+ try {
+ mICarTaskViewHost.createLaunchRootTask(displayId, embedHomeTask, embedRecentsTask,
+ embedAssistantTask);
+ } catch (RemoteException exception) {
+ Slogf.e(TAG, "exception in createRootTask", exception);
+ }
+ }
+
/** Release the resources associated with this task view. */
@ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
+ @MainThread
public void release() {
getHolder().removeCallback(mSurfaceCallbackHandler);
try {
mICarTaskViewHost.release();
- mTaskInfo = null;
} catch (DeadObjectException e) {
Slogf.w(TAG, "TaskView's host has already died", e);
} catch (RemoteException e) {
diff --git a/car-lib/src/android/car/app/RootTaskStackManager.java b/car-lib/src/android/car/app/RootTaskStackManager.java
new file mode 100644
index 0000000..987be70
--- /dev/null
+++ b/car-lib/src/android/car/app/RootTaskStackManager.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.view.SurfaceControl;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+final class RootTaskStackManager {
+ private ActivityManager.RunningTaskInfo mRootTask;
+ private final LinkedHashMap<Integer, ActivityManager.RunningTaskInfo> mChildrenTaskStack =
+ new LinkedHashMap<>();
+
+ void taskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ // The first call to onTaskAppeared() is always for the root-task.
+ if (mRootTask == null) {
+ mRootTask = taskInfo;
+ return;
+ }
+
+ mChildrenTaskStack.put(taskInfo.taskId, taskInfo);
+ }
+
+ void taskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask == null || mRootTask.taskId == taskInfo.taskId) {
+ return;
+ }
+ if (taskInfo.isVisible() && mChildrenTaskStack.containsKey(taskInfo.taskId)) {
+ // Remove the task and insert again so that it jumps to the end of
+ // the queue.
+ mChildrenTaskStack.remove(taskInfo.taskId);
+ mChildrenTaskStack.put(taskInfo.taskId, taskInfo);
+ }
+ }
+
+ void taskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mRootTask == null || mRootTask.taskId == taskInfo.taskId) {
+ return;
+ }
+
+ if (mChildrenTaskStack.containsKey(taskInfo.taskId)) {
+ mChildrenTaskStack.remove(taskInfo.taskId);
+ }
+ }
+
+ @Nullable
+ ActivityManager.RunningTaskInfo getTopTask() {
+ if (mChildrenTaskStack.isEmpty()) {
+ return null;
+ }
+ ActivityManager.RunningTaskInfo topTask = null;
+ Iterator<ActivityManager.RunningTaskInfo> iterator = mChildrenTaskStack.values().iterator();
+ while (iterator.hasNext()) {
+ topTask = iterator.next();
+ }
+ return topTask;
+ }
+}
diff --git a/car-lib/src/android/car/evs/CarEvsManager.java b/car-lib/src/android/car/evs/CarEvsManager.java
index ae8c495..2b6c2e1 100644
--- a/car-lib/src/android/car/evs/CarEvsManager.java
+++ b/car-lib/src/android/car/evs/CarEvsManager.java
@@ -35,8 +35,10 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.SparseArray;
import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+import com.android.car.internal.evs.CarEvsUtils;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
@@ -44,6 +46,8 @@
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
/**
* Provides an application interface for interativing with the Extended View System service.
@@ -62,8 +66,9 @@
private final ICarEvsService mService;
private final Object mStreamLock = new Object();
+ // This array maintains mappings between service type and its client.
@GuardedBy("mStreamLock")
- private CarEvsStreamCallback mStreamCallback;
+ private SparseArray<CarEvsStreamCallback> mStreamCallbacks = new SparseArray<>();
@GuardedBy("mStreamLock")
private Executor mStreamCallbackExecutor;
@@ -329,8 +334,7 @@
}
synchronized (mStreamLock) {
- mStreamCallback = null;
- mStreamCallbackExecutor = null;
+ stopVideoStreamLocked();
}
}
@@ -491,7 +495,10 @@
* {@link com.android.car.ICarEvsStreamCallback} across the binder interface.
*/
private static class CarEvsStreamListenerToService extends ICarEvsStreamCallback.Stub {
+ private static final int DEFAULT_STREAM_EVENT_WAIT_TIMEOUT_IN_SEC = 1;
private final WeakReference<CarEvsManager> mManager;
+ private final Semaphore mStreamEventOccurred = new Semaphore(/* permits= */ 0);
+ private @CarEvsStreamEvent int mLastStreamEvent;
CarEvsStreamListenerToService(CarEvsManager manager) {
mManager = new WeakReference<>(manager);
@@ -499,6 +506,9 @@
@Override
public void onStreamEvent(@CarEvsStreamEvent int event) {
+ mLastStreamEvent = event;
+ mStreamEventOccurred.release();
+
CarEvsManager manager = mManager.get();
if (manager != null) {
manager.handleStreamEvent(event);
@@ -512,6 +522,29 @@
manager.handleNewFrame(buffer);
}
}
+
+ public boolean waitForStreamEvent(@CarEvsStreamEvent int expected) {
+ return waitForStreamEvent(expected, DEFAULT_STREAM_EVENT_WAIT_TIMEOUT_IN_SEC);
+ }
+
+ public boolean waitForStreamEvent(@CarEvsStreamEvent int expected, int timeoutInSeconds) {
+ while (true) {
+ try {
+ if (!mStreamEventOccurred.tryAcquire(timeoutInSeconds, TimeUnit.SECONDS)) {
+ Slogf.w(TAG, "Timer for a new stream event expired.");
+ return false;
+ }
+
+ if (mLastStreamEvent == expected) {
+ return true;
+ }
+ } catch (InterruptedException e) {
+ Slogf.w(TAG, "Interrupted while waiting for an event %d.\nException = %s",
+ expected, Log.getStackTraceString(e));
+ return false;
+ }
+ }
+ }
}
/**
@@ -522,19 +555,21 @@
* @param event {@link #CarEvsStreamEvent} from the service this manager subscribes to.
*/
private void handleStreamEvent(@CarEvsStreamEvent int event) {
+ synchronized(mStreamLock) {
+ handleStreamEventLocked(event);
+ }
+ }
+
+ @GuardedBy("mStreamLock")
+ private void handleStreamEventLocked(@CarEvsStreamEvent int event) {
if (DBG) {
Slogf.d(TAG, "Received: " + event);
}
- final CarEvsStreamCallback callback;
- final Executor executor;
- synchronized (mStreamLock) {
- callback = mStreamCallback;
- executor = mStreamCallbackExecutor;
- }
-
+ CarEvsStreamCallback callback = mStreamCallbacks.get(CarEvsUtils.getTag(event));
+ Executor executor = mStreamCallbackExecutor;
if (callback != null) {
- executor.execute(() -> callback.onStreamEvent(event));
+ executor.execute(() -> callback.onStreamEvent(CarEvsUtils.getValue(event)));
} else if (DBG) {
Slogf.w(TAG, "No client seems active; a current stream event is ignored.");
}
@@ -556,7 +591,7 @@
final CarEvsStreamCallback callback;
final Executor executor;
synchronized (mStreamLock) {
- callback = mStreamCallback;
+ callback = mStreamCallbacks.get(CarEvsUtils.getTag(buffer.getId()));
executor = mStreamCallbackExecutor;
}
@@ -571,6 +606,36 @@
}
}
+
+ /** Stops all active stream callbacks. */
+ @GuardedBy("mStreamLock")
+ private void stopVideoStreamLocked() {
+ if (mStreamCallbacks.size() < 1) {
+ Slogf.i(TAG, "No stream to stop.");
+ return;
+ }
+
+ try {
+ mService.stopVideoStream(mStreamListenerToService);
+ } catch (RemoteException err) {
+ handleRemoteExceptionFromCarService(err);
+ }
+
+ // Wait for a confirmation.
+ if (!mStreamListenerToService.waitForStreamEvent(STREAM_EVENT_STREAM_STOPPED)) {
+ Slogf.w(TAG, "EVS did not notify us that target streams are stopped " +
+ "before a time expires.");
+ }
+
+ // Notify clients that streams are stopped.
+ handleStreamEventLocked(STREAM_EVENT_STREAM_STOPPED);
+
+ // We're not interested in frames and events anymore. The client can safely assume
+ // the service is stopped properly.
+ mStreamCallbacks.clear();
+ mStreamCallbackExecutor = null;
+ }
+
/**
* Returns a consumed {@link android.car.evs.CarEvsBufferDescriptor}.
*
@@ -660,7 +725,7 @@
Objects.requireNonNull(callback);
synchronized (mStreamLock) {
- mStreamCallback = callback;
+ mStreamCallbacks.put(type, callback);
mStreamCallbackExecutor = executor;
}
@@ -682,21 +747,7 @@
@AddedInOrBefore(majorVersion = 33)
public void stopVideoStream() {
synchronized (mStreamLock) {
- if (mStreamCallback == null) {
- Slogf.e(TAG, "The service has not started yet.");
- return;
- }
-
- // We're not interested in frames and events anymore. The client can safely assume
- // the service is stopped properly.
- mStreamCallback = null;
- mStreamCallbackExecutor = null;
- }
-
- try {
- mService.stopVideoStream(mStreamListenerToService);
- } catch (RemoteException err) {
- handleRemoteExceptionFromCarService(err);
+ stopVideoStreamLocked();
}
}
diff --git a/car-lib/src/android/car/hardware/CarPropertyValue.java b/car-lib/src/android/car/hardware/CarPropertyValue.java
index 9d8fd04..2ebb004 100644
--- a/car-lib/src/android/car/hardware/CarPropertyValue.java
+++ b/car-lib/src/android/car/hardware/CarPropertyValue.java
@@ -51,6 +51,7 @@
private final int mPropertyId;
private final int mAreaId;
+ private final int mStatus;
private final long mTimestampNanos;
private final T mValue;
@@ -123,10 +124,7 @@
* @hide
*/
public CarPropertyValue(int propertyId, int areaId, long timestampNanos, T value) {
- mPropertyId = propertyId;
- mAreaId = areaId;
- mTimestampNanos = timestampNanos;
- mValue = value;
+ this(propertyId, areaId, CarPropertyValue.STATUS_AVAILABLE, timestampNanos, value);
}
/**
@@ -136,14 +134,9 @@
*/
@Deprecated
public CarPropertyValue(int propertyId, int areaId, int status, long timestampNanos, T value) {
- if (status != STATUS_AVAILABLE) {
- throw new IllegalArgumentException(
- "car property value with property ID: "
- + VehiclePropertyIds.toString(propertyId) + ", areaID: " + areaId
- + " must have available status, this should not happen.");
- }
mPropertyId = propertyId;
mAreaId = areaId;
+ mStatus = status;
mTimestampNanos = timestampNanos;
mValue = value;
}
@@ -158,6 +151,7 @@
public CarPropertyValue(Parcel in) {
mPropertyId = in.readInt();
mAreaId = in.readInt();
+ mStatus = in.readInt();
mTimestampNanos = in.readLong();
String valueClassName = in.readString();
Class<?> valueClass;
@@ -202,6 +196,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPropertyId);
dest.writeInt(mAreaId);
+ dest.writeInt(mStatus);
dest.writeLong(mTimestampNanos);
Class<?> valueClass = mValue == null ? null : mValue.getClass();
@@ -246,11 +241,11 @@
/**
* @return Status of {@code CarPropertyValue}
*
- * @deprecated This will always return {@link #STATUS_AVAILABLE}.
+ * @deprecated This should be added back in next major Android release.
*/
@AddedInOrBefore(majorVersion = 33)
public @PropertyStatus int getStatus() {
- return STATUS_AVAILABLE;
+ return mStatus;
}
/**
@@ -268,7 +263,12 @@
}
/**
- * @return Value of {@code CarPropertyValue}
+ * Returns the value for {@code CarPropertyValue}.
+ *
+ * <p>
+ * <b>Note:</b>Caller must check the value of {@link #getStatus()}. Only use
+ * {@link #getValue()} when {@link #getStatus()} is {@link #STATUS_AVAILABLE}. If not,
+ * {@link #getValue()} is meaningless.
*/
@NonNull
@AddedInOrBefore(majorVersion = 33)
@@ -283,6 +283,7 @@
+ "mPropertyId=0x" + toHexString(mPropertyId)
+ ", propertyName=" + VehiclePropertyIds.toString(mPropertyId)
+ ", mAreaId=0x" + toHexString(mAreaId)
+ + ", mStatus=" + mStatus
+ ", mTimestampNanos=" + mTimestampNanos
+ ", mValue=" + mValue
+ '}';
@@ -291,7 +292,7 @@
/** Generates hash code for this instance. */
@Override
public int hashCode() {
- return Objects.hash(mPropertyId, mAreaId, mTimestampNanos, mValue);
+ return Objects.hash(mPropertyId, mAreaId, mStatus, mTimestampNanos, mValue);
}
/** Checks equality with passed {@code object}. */
@@ -305,6 +306,7 @@
}
CarPropertyValue<?> carPropertyValue = (CarPropertyValue<?>) object;
return mPropertyId == carPropertyValue.mPropertyId && mAreaId == carPropertyValue.mAreaId
+ && mStatus == carPropertyValue.mStatus
&& mTimestampNanos == carPropertyValue.mTimestampNanos
&& Objects.equals(mValue, carPropertyValue.mValue);
}
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index 6a160cf..8a97a6e 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -1154,11 +1154,13 @@
* <p>
* <b>Note:</b> If listener registers a callback for updates for a property for the first time,
* it will receive the property's current value via a change event upon registration if the
- * property is currently available for reading.
+ * property's value is currently available for reading. If the property is currently not
+ * available for reading or in error state, a property change event with a unavailable or
+ * error status will be generated.
*
- * <p>For properties that might be unavailable for reading because their power state is off,
- * property change events containing the property's initial value will be generated once their
- * power state is on.
+ * <p>For properties that might be unavailable for reading because their power state
+ * is off, property change events containing the property's initial value will be generated
+ * once their power state is on.
*
* <p>If {@code updateRateHz} is higher than {@link CarPropertyConfig#getMaxSampleRate()}, it
* will be registered with max sample {@code updateRateHz}.
@@ -1167,11 +1169,18 @@
* will be registered with min sample {@code updateRateHz}.
*
* <p>
- * <b>Note:</b> A property change event will only happen when the property is available. Caller
- * must never depend on the change event to check property's availability. For properties that
- * might be unavailable because they depend on certain power state, caller should subscribe
- * to the power state property (e.g. {@link VehiclePropertyIds#HVAC_POWER_ON} for hvac dependant
- * properties) to decide this property's availability.
+ * <b>Note:</b>Caller must check the value of {@link CarPropertyValue#getStatus()} for property
+ * change events and only use {@link CarPropertyValue#getValue()} when
+ * {@link CarPropertyValue#getStatus()} is {@link CarPropertyValue#STATUS_AVAILABLE}. If not,
+ * the {@link CarPropertyValue#getValue()} is meaningless.
+ *
+ * <p>
+ * <b>Note:</b>A property change event may/may not happen when the property's status
+ * changes. Caller should not depend on the change event to check property's status. For
+ * properties that might be unavailable because they depend on certain power state, caller
+ * should subscribe to the power state property (e.g.
+ * {@link VehiclePropertyIds#HVAC_POWER_ON} for hvac power dependent properties) to decide this
+ * property's availability.
*
* @param carPropertyEventCallback the CarPropertyEventCallback to be registered
* @param propertyId the property ID to subscribe
@@ -1523,7 +1532,8 @@
}
return mService.getProperty(propertyId, areaId);
});
- return propValue != null;
+ return (propValue != null
+ && propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
} catch (ServiceSpecificException e) {
@@ -1580,7 +1590,7 @@
@AddedInOrBefore(majorVersion = 33)
public boolean getBooleanProperty(int propertyId, int areaId) {
CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, propertyId, areaId);
- return carProp != null ? carProp.getValue() : false;
+ return handleNullAndPropertyStatus(carProp, areaId, false);
}
/**
@@ -1609,7 +1619,7 @@
@AddedInOrBefore(majorVersion = 33)
public float getFloatProperty(int propertyId, int areaId) {
CarPropertyValue<Float> carProp = getProperty(Float.class, propertyId, areaId);
- return carProp != null ? carProp.getValue() : 0f;
+ return handleNullAndPropertyStatus(carProp, areaId, 0f);
}
/**
@@ -1638,7 +1648,7 @@
@AddedInOrBefore(majorVersion = 33)
public int getIntProperty(int propertyId, int areaId) {
CarPropertyValue<Integer> carProp = getProperty(Integer.class, propertyId, areaId);
- return carProp != null ? carProp.getValue() : 0;
+ return handleNullAndPropertyStatus(carProp, areaId, 0);
}
/**
@@ -1668,7 +1678,8 @@
@AddedInOrBefore(majorVersion = 33)
public int[] getIntArrayProperty(int propertyId, int areaId) {
CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, propertyId, areaId);
- return carProp != null ? toIntArray(carProp.getValue()) : new int[0];
+ Integer[] res = handleNullAndPropertyStatus(carProp, areaId, new Integer[0]);
+ return toIntArray(res);
}
private static int[] toIntArray(Integer[] input) {
@@ -1680,6 +1691,30 @@
return arr;
}
+ private <T> T handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId,
+ T defaultValue) {
+ if (propertyValue == null) {
+ return defaultValue;
+ }
+
+ // Keeps the same behavior as android R.
+ if (mAppTargetSdk < Build.VERSION_CODES.S) {
+ return propertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE
+ ? propertyValue.getValue() : defaultValue;
+ }
+
+ // throws new exceptions in android S.
+ switch (propertyValue.getStatus()) {
+ case CarPropertyValue.STATUS_ERROR:
+ throw new CarInternalErrorException(propertyValue.getPropertyId(), areaId);
+ case CarPropertyValue.STATUS_UNAVAILABLE:
+ throw new PropertyNotAvailableException(propertyValue.getPropertyId(),
+ areaId, /*vendorErrorCode=*/0);
+ default:
+ return propertyValue.getValue();
+ }
+ }
+
@FunctionalInterface
private interface RemoteCallable<V> {
V call() throws RemoteException;
@@ -1835,9 +1870,24 @@
Trace.beginSection("getProperty-" + propertyId + "/" + areaId);
try {
- return (CarPropertyValue<E>) (runSyncOperation(() -> {
+ CarPropertyValue<E> carPropertyValue = (CarPropertyValue<E>) (runSyncOperation(() -> {
return mService.getProperty(propertyId, areaId);
}));
+ if (carPropertyValue == null) {
+ return null;
+ }
+ if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_UNAVAILABLE) {
+ throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE,
+ "getProperty returned value with UNAVAILABLE status: "
+ + carPropertyValue);
+ } else if (carPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
+ throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR,
+ "getProperty returned value with error or unknown status: "
+ + carPropertyValue);
+ }
+ }
+ return carPropertyValue;
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
} catch (ServiceSpecificException e) {
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 64c9f51..735897b 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -1688,9 +1688,16 @@
@Override
@AddedInOrBefore(majorVersion = 33)
public void onCarDisconnected() {
- if (mService != null && !mCarVolumeCallbacks.isEmpty()) {
+ if (mService == null) {
+ return;
+ }
+
+ if (!mCarVolumeCallbacks.isEmpty()) {
unregisterVolumeCallback();
}
+ if (!mCarVolumeEventCallbacks.isEmpty()) {
+ unregisterVolumeGroupEventCallback();
+ }
}
/** @hide */
diff --git a/car-lib/src/com/android/car/internal/evs/CarEvsUtils.java b/car-lib/src/com/android/car/internal/evs/CarEvsUtils.java
new file mode 100644
index 0000000..c1dfebd
--- /dev/null
+++ b/car-lib/src/com/android/car/internal/evs/CarEvsUtils.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.internal.evs;
+
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_DRIVERVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_USER_DEFINED;
+
+import android.car.builtin.util.Slogf;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
+
+/**
+ * This class provide utility methods for CarEvsService clients.
+ *
+ * @hide
+ */
+public final class CarEvsUtils {
+ private static final String TAG = CarEvsUtils.class.getSimpleName();
+
+ // To identify the origin of frame buffers and stream events, CarEvsService tags them with their
+ // origin service type in 8-MSB of the frame buffer id and the stream event id. These constants
+ // are used to implement this tagging operation.
+ private static final int TAG_BIT_LEFT_SHIFT = 24;
+ private static final int DATA_BIT_MASK = ~(0xFF << TAG_BIT_LEFT_SHIFT);
+
+ private CarEvsUtils() {}
+
+ public static @CarEvsServiceType int convertToServiceType(String type) {
+ switch (type) {
+ case "REARVIEW":
+ return SERVICE_TYPE_REARVIEW;
+ case "SURROUNDVIEW":
+ return SERVICE_TYPE_SURROUNDVIEW;
+ case "FRONTVIEW":
+ return SERVICE_TYPE_FRONTVIEW;
+ case "LEFTVIEW":
+ return SERVICE_TYPE_LEFTVIEW;
+ case "RIGHTVIEW":
+ return SERVICE_TYPE_RIGHTVIEW;
+ case "DRIVERVIEW":
+ return SERVICE_TYPE_DRIVERVIEW;
+ case "FRONT_PASSENGERSVIEW":
+ return SERVICE_TYPE_FRONT_PASSENGERSVIEW;
+ case "REAR_PASSENGERSVIEW":
+ return SERVICE_TYPE_REAR_PASSENGERSVIEW;
+ default:
+ Slogf.w(TAG, "USER_DEFINED will be returned for a unknown service type " + type);
+ // fall through
+ case "USER_DEFINED":
+ return SERVICE_TYPE_USER_DEFINED;
+ }
+ }
+
+ public static String convertToString(@CarEvsServiceType int type) {
+ switch (type) {
+ case SERVICE_TYPE_REARVIEW:
+ return "REARVIEW";
+ case SERVICE_TYPE_SURROUNDVIEW:
+ return "SURROUNDVIEW";
+ case SERVICE_TYPE_FRONTVIEW:
+ return "FRONTVIEW";
+ case SERVICE_TYPE_LEFTVIEW:
+ return "LEFTVIEW";
+ case SERVICE_TYPE_RIGHTVIEW:
+ return "RIGHTVIEW";
+ case SERVICE_TYPE_DRIVERVIEW:
+ return "DRIVERVIEW";
+ case SERVICE_TYPE_FRONT_PASSENGERSVIEW:
+ return "FRONT_PASSENGERVIEW";
+ case SERVICE_TYPE_REAR_PASSENGERSVIEW:
+ return "REAR_PASSENGERVIEW";
+ case SERVICE_TYPE_USER_DEFINED:
+ return "USER_DEFINED";
+ default:
+ return "Unknown type= + type";
+ }
+ }
+
+ /**
+ * Extracts a service type from a given value and returns it.
+ *
+ * @param value This should be either an event or CarEvsBufferDescriptor id that are sent by
+ * ICarEvsStreamCallback.onStreamEvent() and ICarEvsStreamCallback.onNewFrame()
+ * callbacks respectively.
+ * @return A service type embedded in 8-MSB of a given value.
+ */
+ public static @CarEvsServiceType int getTag(int value) {
+ return value >> TAG_BIT_LEFT_SHIFT;
+ }
+
+ /**
+ * Extracts an actual buffer id or an event id from a given value and returns it.
+ *
+ * @param value This should be either an event or CarEvsBufferDescriptor id that are sent by
+ * ICarEvsStreamCallback.onStreamEvent() and ICarEvsStreamCallback.onNewFrame()
+ * callbacks respectively.
+ * @return A buffer id or an event.
+ */
+ public static int getValue(int value) {
+ return value &= DATA_BIT_MASK;
+ }
+
+ /**
+ * Embeds a given tag in 8-MSB of a given value and returns it.
+ *
+ * @param tag Additional information to identify the origin of a given value that is either a
+ * buffer id or an event.
+ * @param value This should be either an event or CarEvsBufferDescriptor id that are sent by
+ * ICarEvsStreamCallback.onStreamEvent() and ICarEvsStreamCallback.onNewFrame()
+ * callbacks respectively.
+ * @return 32-bit integer that contains a tag in 8-MSB and a value in the rest.
+ */
+ public static int putTag(int tag, int value) {
+ if (tag > 0xFF) {
+ Slogf.w(TAG, "A given tag %d is greater than 0xFF. Only 8-LSB will be effective.", tag);
+ }
+ return ((tag & 0xFF) << TAG_BIT_LEFT_SHIFT) | (value & DATA_BIT_MASK);
+ }
+}
diff --git a/car-lib/src/com/android/car/internal/property/CarPropertyHelper.java b/car-lib/src/com/android/car/internal/property/CarPropertyHelper.java
index 0aad8e0..70b816e 100644
--- a/car-lib/src/com/android/car/internal/property/CarPropertyHelper.java
+++ b/car-lib/src/com/android/car/internal/property/CarPropertyHelper.java
@@ -18,6 +18,8 @@
import android.annotation.SuppressLint;
import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.VehicleHalStatusCode;
import android.car.hardware.property.VehicleHalStatusCode.VehicleHalStatusCodeInt;
import android.util.Log;
import android.util.SparseArray;
@@ -117,6 +119,25 @@
return vhalErrorCode >>> VENDOR_ERROR_CODE_SHIFT;
}
+ /**
+ * Returns {@code true} if {@code vehicleHalStatusCode} is one of the not available
+ * {@link VehicleHalStatusCode} values}. Otherwise returns {@code false}.
+ */
+ public static boolean isNotAvailableVehicleHalStatusCode(
+ @VehicleHalStatusCodeInt int vehicleHalStatusCode) {
+ switch (vehicleHalStatusCode) {
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE:
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED:
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW:
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH:
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_POOR_VISIBILITY:
+ case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private static SparseArray<String> cachePropertyIdsToNameMapping() {
SparseArray<String> propertyIdsToNameMapping = sPropertyIdToPropertyNameHolder.get();
if (propertyIdsToNameMapping == null) {
@@ -167,4 +188,42 @@
private static boolean isVendorProperty(int propertyId) {
return (propertyId & VEHICLE_PROPERTY_GROUP_MASK) == VEHICLE_PROPERTY_GROUP_VENDOR;
}
+
+
+ /**
+ * Gets the default value for a {@link CarPropertyValue} class type.
+ */
+ public static <T> T getDefaultValue(Class<T> clazz) {
+ if (clazz.equals(Boolean.class)) {
+ return (T) Boolean.FALSE;
+ }
+ if (clazz.equals(Integer.class)) {
+ return (T) Integer.valueOf(0);
+ }
+ if (clazz.equals(Long.class)) {
+ return (T) Long.valueOf(0);
+ }
+ if (clazz.equals(Float.class)) {
+ return (T) Float.valueOf(0f);
+ }
+ if (clazz.equals(Integer[].class)) {
+ return (T) new Integer[0];
+ }
+ if (clazz.equals(Long[].class)) {
+ return (T) new Long[0];
+ }
+ if (clazz.equals(Float[].class)) {
+ return (T) new Float[0];
+ }
+ if (clazz.equals(byte[].class)) {
+ return (T) new byte[0];
+ }
+ if (clazz.equals(Object[].class)) {
+ return (T) new Object[0];
+ }
+ if (clazz.equals(String.class)) {
+ return (T) new String("");
+ }
+ throw new IllegalArgumentException("Unexpected class: " + clazz);
+ }
}
diff --git a/car-maps-placeholder/AndroidManifest.xml b/car-maps-placeholder/AndroidManifest.xml
index a28aa9a..a5c2b95 100644
--- a/car-maps-placeholder/AndroidManifest.xml
+++ b/car-maps-placeholder/AndroidManifest.xml
@@ -26,7 +26,8 @@
android:launchMode="singleTask"
android:label="@string/app_name"
android:resizeableActivity="true"
- android:exported="true">
+ android:exported="true"
+ android:excludeFromRecents="true">
<meta-data android:name="distractionOptimized" android:value="true"/>
<intent-filter android:priority="-1000">
<action android:name="android.intent.action.MAIN"/>
diff --git a/car-test-lib/src/android/car/test/ApiCheckerRule.java b/car-test-lib/src/android/car/test/ApiCheckerRule.java
index 056faef..80050a7 100644
--- a/car-test-lib/src/android/car/test/ApiCheckerRule.java
+++ b/car-test-lib/src/android/car/test/ApiCheckerRule.java
@@ -270,6 +270,18 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ base.evaluate();
+ }
+ };
+ }
+
+ // TODO(b/285930588):ApiCheckerRule is no longer required. But the code can be useful,
+ // Currently disabling the rule. As part of the bug, more investigation is required how to
+ // clean up API Checker Rule.
+ public Statement applyOld(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
mTestMethodName = description.getMethodName();
try {
evaluateInternal();
diff --git a/car-usb-handler/AndroidManifest.xml b/car-usb-handler/AndroidManifest.xml
index 4237c49..f9962ac 100644
--- a/car-usb-handler/AndroidManifest.xml
+++ b/car-usb-handler/AndroidManifest.xml
@@ -43,7 +43,8 @@
android:directBootAware="true">
<activity android:name=".UsbHostManagementActivity"
android:theme="@android:style/Theme.DeviceDefault.Dialog"
- android:launchMode="standard">
+ android:launchMode="standard"
+ android:excludeFromRecents="true">
<meta-data
android:name="distractionOptimized"
android:value="true"/>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_avatar_icon_outline.xml
similarity index 79%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
copy to car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_avatar_icon_outline.xml
index 36ee983..82471fb0 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
+++ b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_avatar_icon_outline.xml
@@ -14,6 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_surface_variant</color>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+</shape>
diff --git a/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_background.xml b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_background.xml
new file mode 100644
index 0000000..69ae4aa
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_background.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:state_pressed="true">
+ <shape android:shape="rectangle">
+ <solid android:color="#8A94CBFF"/>
+ <stroke android:width="4dp"
+ android:color="#94CBFF"/>
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape android:shape="rectangle">
+ <solid android:color="#3D94CBFF"/>
+ <stroke android:width="8dp"
+ android:color="#94CBFF"/>
+ </shape>
+ </item>
+ <item android:state_activated="true">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorControlHighlight"/>
+ </shape>
+ </item>
+ <item>
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:colorAccent"/>
+ </shape>
+ </item>
+ </ripple>
+ </item>
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/strings.xml b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_divider.xml
similarity index 63%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/strings.xml
rename to car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_divider.xml
index 678346b..cc9691e 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/strings.xml
+++ b/car_product/app_overlays/car-ui-customizations/res/drawable/car_ui_list_item_divider.xml
@@ -1,12 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2022 Google Inc.
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 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
+ 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,
@@ -14,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_title">Car launcher reference</string>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#33FFFFFF" />
+</shape>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_2.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_2.xml
new file mode 100644
index 0000000..17da9f2
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_2.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2019 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.
+ -->
+
+<!-- Note that in this RRO layout, ViewStubs are used. Depending on the type of list item (i.e., how
+ many tap targets are used) some of these ViewStubs will be inflated with a View hierarchy which
+ contains a top level tap target (or two) and descendant ui/text elements. If the version of
+ car-ui-lib that the app is compiled with doesn't support car_ui_list_item_2, it will default to
+ the static implementation's layout which is car_ui_list_item which doesn't support talkback. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tag="carUiListItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="116dp"
+ android:orientation="horizontal">
+ <ViewStub
+ android:id="@+id/car_ui_list_item_touch_interceptor"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout="@layout/car_ui_list_item_touch_interceptor"/>
+ <ViewStub
+ android:id="@+id/car_ui_list_item_reduced_touch_interceptor"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout="@layout/car_ui_list_item_reduced_touch_interceptor"/>
+ <ViewStub
+ android:id="@+id/car_ui_list_item_action_container_touch_interceptor"
+ android:layout_height="match_parent"
+ android:layout_width="112dp"
+ android:layout="@layout/car_ui_list_item_action_container_touch_interceptor"/>
+</LinearLayout>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action.xml
new file mode 100644
index 0000000..6630055
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <View
+ android:id="@+id/car_ui_list_item_action_divider"
+ android:layout_width="1dp"
+ android:layout_height="60dp"
+ android:layout_gravity="start|center_vertical"
+ android:background="@drawable/car_ui_list_item_divider" />
+ <Switch
+ android:id="@+id/car_ui_list_item_switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="false"
+ android:focusable="false" />
+ <CheckBox
+ android:id="@+id/car_ui_list_item_checkbox_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="false"
+ android:focusable="false" />
+ <RadioButton
+ android:id="@+id/car_ui_list_item_radio_button_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="false"
+ android:focusable="false"
+ android:contentDescription="Radio Button"/>
+ <ImageView
+ android:id="@+id/car_ui_list_item_supplemental_icon"
+ android:layout_width="44dp"
+ android:layout_height="44dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"/>
+</merge>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action_container_touch_interceptor.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action_container_touch_interceptor.xml
new file mode 100644
index 0000000..cd06dfd
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_action_container_touch_interceptor.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<!-- This touch interceptor is sized and positioned to encompass the action container -->
+<com.android.car.ui.SecureView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/car_ui_list_item_action_container_touch_interceptor"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/car_ui_list_item_background">
+ <FrameLayout
+ android:id="@+id/car_ui_list_item_action_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <include layout="@layout/car_ui_list_item_action"/>
+ </FrameLayout>
+</com.android.car.ui.SecureView>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_icon_title_body_container.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_icon_title_body_container.xml
new file mode 100644
index 0000000..550fe94
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_icon_title_body_container.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/car_ui_list_item_icon_container"
+ android:layout_width="112dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <ImageView
+ android:id="@+id/car_ui_list_item_icon"
+ android:layout_width="44dp"
+ android:layout_height="44dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter" />
+ <ImageView
+ android:id="@+id/car_ui_list_item_content_icon"
+ android:layout_width="112dp"
+ android:layout_height="112dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter" />
+ <ImageView
+ android:id="@+id/car_ui_list_item_avatar_icon"
+ android:background="@drawable/car_ui_list_item_avatar_icon_outline"
+ android:layout_width="44dp"
+ android:layout_height="44dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter" />
+ </FrameLayout>
+ <CarUiTextView
+ android:id="@+id/car_ui_list_item_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:singleLine="false"
+ android:textAppearance="@style/TextAppearance.CarUi.ListItem"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toTopOf="@+id/car_ui_list_item_body"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="packed"
+ app:layout_goneMarginStart="24dp" />
+ <CarUiTextView
+ android:id="@+id/car_ui_list_item_body"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:textAppearance="@style/TextAppearance.CarUi.ListItem.Body"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
+ app:layout_constraintTop_toBottomOf="@+id/car_ui_list_item_title"
+ app:layout_goneMarginStart="24dp" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_reduced_touch_interceptor.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_reduced_touch_interceptor.xml
new file mode 100644
index 0000000..7cfa875
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_reduced_touch_interceptor.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<!-- This touch interceptor does not include the action container -->
+<com.android.car.ui.SecureView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/car_ui_list_item_reduced_touch_interceptor"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/car_ui_list_item_background">
+ <include layout="@layout/car_ui_list_item_icon_title_body_container"/>
+</com.android.car.ui.SecureView>
diff --git a/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_touch_interceptor.xml b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_touch_interceptor.xml
new file mode 100644
index 0000000..61eb66a
--- /dev/null
+++ b/car_product/app_overlays/car-ui-customizations/res/layout/car_ui_list_item_touch_interceptor.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<!-- This touch interceptor spans the entire list item -->
+<com.android.car.ui.SecureView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/car_ui_list_item_touch_interceptor"
+ android:background="@drawable/car_ui_list_item_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <include
+ layout="@layout/car_ui_list_item_icon_title_body_container"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"/>
+ <FrameLayout
+ android:id="@+id/car_ui_list_item_action_container"
+ android:layout_width="112dp"
+ android:layout_height="match_parent">
+ <include layout="@layout/car_ui_list_item_action"/>
+ </FrameLayout>
+ </LinearLayout>
+</com.android.car.ui.SecureView>
diff --git a/car_product/app_overlays/car-ui-customizations/res/values/attrs.xml b/car_product/app_overlays/car-ui-customizations/res/values/attrs.xml
index 74adbf9..68d6530 100644
--- a/car_product/app_overlays/car-ui-customizations/res/values/attrs.xml
+++ b/car_product/app_overlays/car-ui-customizations/res/values/attrs.xml
@@ -49,6 +49,12 @@
<attr name="layout_constraintHorizontal_bias" format="float"/>
<attr name="layout_constraintVertical_bias" format="float"/>
+ <attr name="layout_constraintVertical_chainStyle" format="enum">
+ <enum name="spread" value="0"/>
+ <enum name="spread_inside" value="1"/>
+ <enum name="packed" value="2"/>
+ </attr>
+
<attr name="layout_goneMarginLeft" format="dimension"/>
<attr name="layout_goneMarginTop" format="dimension"/>
<attr name="layout_goneMarginRight" format="dimension"/>
diff --git a/car_product/app_overlays/car-ui-customizations/res/values/styles.xml b/car_product/app_overlays/car-ui-customizations/res/values/styles.xml
index ddce064..078e3a1 100644
--- a/car_product/app_overlays/car-ui-customizations/res/values/styles.xml
+++ b/car_product/app_overlays/car-ui-customizations/res/values/styles.xml
@@ -111,6 +111,13 @@
<item name="android:textColor">@color/car_ui_text_color_secondary</item>
</style>
+ <style name="TextAppearance.CarUi.ListItem" parent="TextAppearance.CarUi.Body1"/>
+
+ <style name="TextAppearance.CarUi.ListItem.Body" parent="TextAppearance.CarUi.Body3">
+ <item name="android:textColor">@color/car_ui_text_color_secondary</item>
+ </style>
+
+
<style name="Widget.CarUi.Toolbar.Container"></style>
<style name="Widget.CarUi.Toolbar.LogoContainer">
<item name="android:paddingEnd">@dimen/car_ui_toolbar_title_logo_padding</item>
diff --git a/car_product/app_overlays/car-ui-customizations/res/xml/overlays.xml b/car_product/app_overlays/car-ui-customizations/res/xml/overlays.xml
index 4bcbc1f..edfcb98 100644
--- a/car_product/app_overlays/car-ui-customizations/res/xml/overlays.xml
+++ b/car_product/app_overlays/car-ui-customizations/res/xml/overlays.xml
@@ -29,6 +29,7 @@
<item target="attr/layout_constraintStart_toStartOf" value="@attr/layout_constraintStart_toStartOf"/>
<item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf"/>
<item target="attr/layout_constraintTop_toTopOf" value="@attr/layout_constraintTop_toTopOf"/>
+ <item target="attr/layout_constraintVertical_chainStyle" value="@attr/layout_constraintVertical_chainStyle"/>
<item target="attr/layout_goneMarginBottom" value="@attr/layout_goneMarginBottom"/>
<item target="attr/layout_goneMarginEnd" value="@attr/layout_goneMarginEnd"/>
<item target="attr/layout_goneMarginLeft" value="@attr/layout_goneMarginLeft"/>
@@ -55,6 +56,9 @@
<item target="dimen/car_ui_scrollbar_separator_margin" value="@dimen/car_ui_scrollbar_separator_margin"/>
<item target="drawable/car_ui_icon_arrow_back" value="@drawable/car_ui_icon_arrow_back"/>
+ <item target="drawable/car_ui_list_item_divider" value="@drawable/car_ui_list_item_divider"/>
+ <item target="drawable/car_ui_list_item_background" value="@drawable/car_ui_list_item_background"/>
+ <item target="drawable/car_ui_list_item_avatar_icon_outline" value="@drawable/car_ui_list_item_avatar_icon_outline"/>
<item target="drawable/car_ui_toolbar_menu_item_icon_background" value="@drawable/car_ui_toolbar_menu_item_icon_background"/>
<item target="drawable/car_ui_toolbar_menu_item_icon_ripple" value="@drawable/car_ui_toolbar_menu_item_icon_ripple"/>
@@ -65,6 +69,21 @@
<item target="id/car_ui_secondary_action" value="@id/car_ui_secondary_action" />
<item target="id/car_ui_secondary_action_concrete" value="@id/car_ui_secondary_action_concrete" />
<item target="id/car_ui_link" value="@id/car_ui_link" />
+ <item target="id/car_ui_list_item_touch_interceptor" value="@id/car_ui_list_item_touch_interceptor"/>
+ <item target="id/car_ui_list_item_reduced_touch_interceptor" value="@id/car_ui_list_item_reduced_touch_interceptor"/>
+ <item target="id/car_ui_list_item_icon_container" value="@id/car_ui_list_item_icon_container"/>
+ <item target="id/car_ui_list_item_icon" value="@id/car_ui_list_item_icon"/>
+ <item target="id/car_ui_list_item_content_icon" value="@id/car_ui_list_item_content_icon"/>
+ <item target="id/car_ui_list_item_avatar_icon" value="@id/car_ui_list_item_avatar_icon"/>
+ <item target="id/car_ui_list_item_title" value="@id/car_ui_list_item_title"/>
+ <item target="id/car_ui_list_item_body" value="@id/car_ui_list_item_body"/>
+ <item target="id/car_ui_list_item_action_container_touch_interceptor" value="@id/car_ui_list_item_action_container_touch_interceptor"/>
+ <item target="id/car_ui_list_item_action_container" value="@id/car_ui_list_item_action_container"/>
+ <item target="id/car_ui_list_item_action_divider" value="@id/car_ui_list_item_action_divider"/>
+ <item target="id/car_ui_list_item_switch_widget" value="@id/car_ui_list_item_switch_widget"/>
+ <item target="id/car_ui_list_item_checkbox_widget" value="@id/car_ui_list_item_checkbox_widget"/>
+ <item target="id/car_ui_list_item_radio_button_widget" value="@id/car_ui_list_item_radio_button_widget"/>
+ <item target="id/car_ui_list_item_supplemental_icon" value="@id/car_ui_list_item_supplemental_icon"/>
<item target="id/car_ui_preference_container_without_widget" value="@id/car_ui_preference_container_without_widget" />
<item target="id/action_widget_container" value="@id/action_widget_container" />
<item target="id/car_ui_toolbar_background" value="@id/car_ui_toolbar_background" />
@@ -91,6 +110,7 @@
<item target="layout/car_ui_alert_dialog_list" value="@layout/car_ui_alert_dialog_list"/>
<item target="layout/car_ui_base_layout_toolbar" value="@layout/car_ui_base_layout_toolbar"/>
+ <item target="layout/car_ui_list_item_2" value="@layout/car_ui_list_item_2"/>
<item target="layout/car_ui_preference" value="@layout/car_ui_preference"/>
<item target="layout/car_ui_preference_category" value="@layout/car_ui_preference_category"/>
<item target="layout/car_ui_preference_footer" value="@layout/car_ui_preference_footer"/>
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index a1f7786..fb18dfa 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -165,6 +165,7 @@
# RROs
PRODUCT_PACKAGES += \
CarPermissionControllerRRO \
+ CarSystemUIRRO \
# CarSystemUIPassengerOverlay is an RRO package required for enabling unique look
# and feel for Passenger(Secondary) User.
@@ -264,7 +265,7 @@
PRODUCT_BOOT_JARS += \
android.car.builtin
-USE_CAR_FRAMEWORK_APEX ?= true
+USE_CAR_FRAMEWORK_APEX ?= false
ifeq ($(USE_CAR_FRAMEWORK_APEX),true)
PRODUCT_PACKAGES += com.android.car.framework
@@ -278,7 +279,6 @@
PRODUCT_HIDDENAPI_STUBS_SYSTEM := android.car-module.stubs.system
PRODUCT_HIDDENAPI_STUBS_TEST := android.car-module.stubs.test
else # !USE_CAR_FRAMEWORK_APEX
- $(warning NOT using CarFramework APEX)
PRODUCT_BOOT_JARS += android.car
PRODUCT_PACKAGES += android.car CarServiceUpdatableNonModule car-frameworks-service-module
PRODUCT_SYSTEM_SERVER_JARS += car-frameworks-service-module
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index be180a2..88cacd3 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -31,7 +31,6 @@
BasicDreams \
CaptivePortalLogin \
CertInstaller \
- DocumentsUI \
DownloadProviderUi \
FusedLocation \
InputDevices \
diff --git a/car_product/build/car_generic_system.mk b/car_product/build/car_generic_system.mk
index 3297937..541981f 100644
--- a/car_product/build/car_generic_system.mk
+++ b/car_product/build/car_generic_system.mk
@@ -78,17 +78,9 @@
packages/services/Car/cpp/evs/manager/aidl/init.evs.rc:$(TARGET_COPY_OUT_SYSTEM)/etc/init/init.evs.rc
endif
-ifeq ($(ENABLE_EVS_SAMPLE), true)
-# ENABLE_EVS_SAMPLE should set be true or their vendor specific equivalents should be included in
-# the device.mk with the corresponding selinux policies
-PRODUCT_PACKAGES += evs_app \
- android.hardware.automotive.evs-default \
- cardisplayproxyd
-include packages/services/Car/cpp/evs/apps/sepolicy/evsapp.mk
-endif # ENABLE_EVS_SAMPLE
-
ifeq ($(ENABLE_CAREVSSERVICE_SAMPLE), true)
-PRODUCT_PACKAGES += CarEvsCameraPreviewApp
+# TODO(b/293332636): Clean up below line when both apps are consolidated into a single app.
+PRODUCT_PACKAGES += CarEvsCameraPreviewApp CarEvsMultiCameraPreviewApp
endif
ifeq ($(ENABLE_REAR_VIEW_CAMERA_SAMPLE), true)
PRODUCT_PACKAGES += SampleRearViewCamera
@@ -100,19 +92,20 @@
# Conditionally enable the telemetry service
ifeq ($(ENABLE_CARTELEMETRY_SERVICE), true)
-PRODUCT_PACKAGES += android.automotive.telemetryd@1.0
+PRODUCT_PACKAGES += android.automotive.telemetryd@1.0 \
+ ScriptExecutor
endif
-ifeq ($(ENABLE_EVS_SAMPLE), true)
-# ENABLE_EVS_SAMPLE should set be true or their vendor specific equivalents should be included in
+# ENABLE_EVS_SAMPLE should be set true or their vendor specific equivalents should be included in
# the device.mk with the corresponding selinux policies
+ifeq ($(ENABLE_EVS_SAMPLE), true)
PRODUCT_PACKAGES += evs_app
-include packages/services/Car/cpp/evs/apps/sepolicy/evsapp.mk
-
# A reference EVS HAL implementation will be added in car_vendor.mk and require AIDL version of
# the automotive display service implementation.
USE_AIDL_DISPLAY_SERVICE := true
-endif # ENABLE_EVS_SAMPLE
+else ifeq ($(ENABLE_SAMPLE_EVS_APP), true)
+PRODUCT_PACKAGES += evs_app
+endif
ifeq ($(USE_AIDL_DISPLAY_SERVICE), true)
PRODUCT_PACKAGES += cardisplayproxyd
diff --git a/car_product/build/car_product.mk b/car_product/build/car_product.mk
index 0a09df3..4a98375 100644
--- a/car_product/build/car_product.mk
+++ b/car_product/build/car_product.mk
@@ -65,4 +65,8 @@
PRODUCT_PACKAGES += CarSystemUIPassengerOverlay
endif # ENABLE_PASSENGER_SYSTEMUI_RRO
+ifneq (,$(filter true,$(ENABLE_EVS_SAMPLE) $(ENABLE_SAMPLE_EVS_APP)))
+include packages/services/Car/cpp/evs/apps/sepolicy/evsapp.mk
+endif
+
$(call inherit-product, device/sample/products/location_overlay.mk)
diff --git a/car_product/build/car_system.mk b/car_product/build/car_system.mk
index 1e48c09..eb39a90 100644
--- a/car_product/build/car_system.mk
+++ b/car_product/build/car_system.mk
@@ -157,7 +157,6 @@
BasicDreams \
CaptivePortalLogin \
CertInstaller \
- DocumentsUI \
DownloadProviderUi \
FusedLocation \
InputDevices \
@@ -238,6 +237,7 @@
# RROs
PRODUCT_PACKAGES += \
CarPermissionControllerRRO \
+ CarSystemUIRRO \
# System Server components
# Order is important: if X depends on Y, then Y should precede X on the list.
@@ -246,7 +246,7 @@
PRODUCT_BOOT_JARS += \
android.car.builtin
-USE_CAR_FRAMEWORK_APEX ?= true
+USE_CAR_FRAMEWORK_APEX ?= false
ifeq ($(USE_CAR_FRAMEWORK_APEX),true)
PRODUCT_PACKAGES += com.android.car.framework
@@ -260,7 +260,6 @@
PRODUCT_HIDDENAPI_STUBS_SYSTEM := android.car-module.stubs.system
PRODUCT_HIDDENAPI_STUBS_TEST := android.car-module.stubs.test
else # !USE_CAR_FRAMEWORK_APEX
- $(warning NOT using CarFramework APEX)
PRODUCT_BOOT_JARS += android.car
PRODUCT_PACKAGES += android.car CarServiceUpdatableNonModule car-frameworks-service-module
PRODUCT_SYSTEM_SERVER_JARS += car-frameworks-service-module
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index eca321f..2bed070 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -469,4 +469,8 @@
<install-in-user-type package="com.android.systemui.md.passenger.car.rro">
<install-in user-type="FULL" />
</install-in-user-type>
+ <!-- Required by RotaryService-->
+ <install-in-user-type package="com.android.car.rotary">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
</config>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/Android.bp b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/Android.bp
index 0f323aa..551a126 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/Android.bp
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/Android.bp
@@ -17,49 +17,29 @@
}
android_app {
- name: "car-ui-lib-portrait-sharedlibrary",
- manifest: "sharedlibrary/src/main/AndroidManifest.xml",
- aaptflags: ["--shared-lib"],
- sdk_version: "current",
- optimize: {
- enabled: false,
- },
- // car-ui-lib-oem-apis is added such that at runtime those classes will be available
- // to the car-ui-lib code which only compiles against them
- static_libs: [
- "car-ui-lib",
- "car-ui-lib-oem-apis",
- ],
- export_package_resources: true,
- overrides: [
- "car-ui-lib-sharedlibrary-vendor",
- ]
-}
-
-android_app {
name: "car-ui-lib-portrait-proxyplugin",
- certificate: "platform",
- min_sdk_version: "28",
- target_sdk_version: "30",
+ aaptflags: ["--shared-lib"],
sdk_version: "current",
manifest: "plugin/src/main/AndroidManifest.xml",
srcs: ["plugin/src/main/java/**/*.java"],
libs: [
- "car-ui-lib-portrait-sharedlibrary",
"android.car-stubs",
],
- static_libs: [
- "car-ui-lib-oem-apis",
- "androidx.annotation_annotation",
- ],
-
+ resource_dirs: ["plugin/res"],
optimize: {
enabled: false,
},
- enforce_uses_libs: false,
- overrides: [
- "car-ui-lib-proxyplugin",
- ]
+ static_libs: [
+ "androidx-constraintlayout_constraintlayout-solver",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.annotation_annotation",
+ "car-portrait-ui-common",
+ "car-ui-lib",
+ "car-ui-lib-oem-apis",
+ ],
+ overrides: [
+ "car-ui-lib-proxyplugin",
+ ]
}
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/color/car_ui_portrait_toolbar_tab_item_selector.xml
similarity index 73%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/color/car_ui_portrait_toolbar_tab_item_selector.xml
index 42b5d04..826a150 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/color/car_ui_portrait_toolbar_tab_item_selector.xml
@@ -14,10 +14,12 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?android:attr/disabledAlpha"
- android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_surface_variant"/>
+ <item android:state_activated="true"
+ android:color="@color/car_on_secondary_container" />
+ <item android:color="@color/car_on_surface_variant" />
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_divider.xml
similarity index 79%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_divider.xml
index 36ee983..b2f8573 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_divider.xml
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_surface_variant</color>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <size android:width="16dp"/>
+</shape>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
similarity index 78%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
index 8ad0860..cddfe08 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
@@ -13,7 +13,8 @@
~ 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.
- -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+ ~
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/car_control_highlight"
+ android:radius="48dp"/>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_base_layout_toolbar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_base_layout_toolbar.xml
new file mode 100644
index 0000000..5168529
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_base_layout_toolbar.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<!-- This is for the two-row version of the toolbar -->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tag="CarUiPortraitBaseLayoutToolbar">
+ <!-- When not in touch mode, if we clear focus in current window, Android will re-focus the
+ first focusable view in the window automatically. Adding a FocusParkingView to the window
+ can fix this issue, because it can take focus, and it is transparent and its default focus
+ highlight is disabled, so it's invisible to the user no matter whether it's focused or not.
+ -->
+ <com.android.car.ui.FocusParkingView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@+id/car_ui_base_layout_content_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@id/car_ui_portrait_base_layout_divider"
+ app:layout_constraintRight_toRightOf="parent"/>
+
+ <com.android.car.ui.FocusArea
+ android:id="@+id/top_part_of_toolbar_focus_area"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_ui_portrait_toolbar_height"
+ app:layout_constraintTop_toTopOf="parent">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/car_ui_portrait_toolbar_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:tag="car_ui_portrait_top_inset">
+ <com.android.car.ui.baselayout.ClickBlockingView
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+ <Space
+ android:id="@+id/car_ui_portrait_toolbar_nav_icon_spacer"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_width"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"/>
+
+ <FrameLayout
+ android:id="@+id/car_ui_portrait_toolbar_nav_icon_container"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_nav_icon_container_size"
+ android:layout_height="@dimen/car_ui_portrait_toolbar_nav_icon_container_size"
+ app:layout_constraintStart_toEndOf="@id/car_ui_portrait_toolbar_nav_icon_spacer"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <ImageView
+ android:id="@+id/car_ui_portrait_toolbar_nav_icon"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_nav_icon_size"
+ android:layout_height="@dimen/car_ui_portrait_toolbar_nav_icon_size"
+ android:layout_margin="@dimen/car_ui_portrait_toolbar_nav_icon_margin"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"
+ android:background="@drawable/car_ui_toolbar_menu_item_icon_ripple"
+ android:tint="?android:attr/textColorPrimary"/>
+ </FrameLayout>
+
+ <Space
+ android:id="@+id/car_ui_portrait_toolbar_logo_spacer"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_nav_icon_container_size"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"/>
+
+ <Space
+ android:id="@+id/car_ui_portrait_toolbar_title_spacer"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/car_ui_portrait_toolbar_width"
+ app:layout_goneMarginStart="0dp"
+ app:layout_constraintHorizontal_bias="0.8"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"/>
+
+ <LinearLayout
+ android:id="@+id/car_ui_portrait_toolbar_title_logo_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="@dimen/car_ui_portrait_toolbar_title_logo_container_padding"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@id/car_ui_portrait_toolbar_logo_spacer"
+ app:layout_constraintEnd_toStartOf="@id/car_ui_portrait_toolbar_title_spacer">
+
+ <ImageView
+ android:id="@+id/car_ui_portrait_toolbar_logo"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_logo_size"
+ android:layout_height="@dimen/car_ui_portrait_toolbar_logo_size"
+ android:layout_marginEnd="@dimen/car_ui_portrait_toolbar_logo_margin"
+ android:layout_gravity="center"
+ android:scaleType="fitXY" />
+
+ <LinearLayout
+ android:id="@+id/car_ui_portrait_toolbar_title_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/car_ui_toolbar_nav_icon_container"
+ app:layout_constraintStart_toEndOf="@id/car_ui_portrait_toolbar_logo">
+ <TextView android:id="@+id/car_ui_portrait_toolbar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:requiresFadingEdge="horizontal"
+ android:fadingEdgeLength="@*android:dimen/car_textview_fading_edge_length"
+ style="@style/TextAppearance.CarUi.Widget.Toolbar.Title"/>
+ <TextView android:id="@+id/car_ui_portrait_toolbar_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:textAlignment="viewStart"
+ android:ellipsize="none"
+ android:requiresFadingEdge="horizontal"
+ android:fadingEdgeLength="@*android:dimen/car_textview_fading_edge_length"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/car_ui_toolbar_search_view_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/car_ui_toolbar_menu_items_container"
+ app:layout_constraintStart_toEndOf="@+id/car_ui_portrait_toolbar_nav_icon_container"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/car_ui_toolbar_menu_items_container"
+ android:divider="@drawable/car_ui_toolbar_menu_item_divider"
+ android:showDividers="beginning|middle|end"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <ProgressBar
+ android:id="@+id/car_ui_toolbar_progress_bar"
+ style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.car.ui.FocusArea>
+
+ <com.android.car.ui.FocusArea
+ android:id="@+id/left_part_of_toolbar_focus_area"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintTop_toBottomOf="@id/top_part_of_toolbar_focus_area"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent">
+
+ <com.chassis.car.ui.plugin.toolbar.TabLayout
+ android:id="@+id/car_ui_portrait_toolbar_tabs"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_width"
+ android:layout_height="match_parent"
+ android:padding="@dimen/car_ui_portrait_toolbar_padding"
+ android:orientation="vertical"/>
+ </com.android.car.ui.FocusArea>
+
+ <!-- Hairline to the right of the tabs -->
+ <View
+ android:id="@+id/car_ui_portrait_base_layout_divider"
+ android:layout_width="1dp"
+ android:layout_height="0dp"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:tag="car_ui_portrait_left_inset"
+ app:layout_constraintLeft_toRightOf="@id/left_part_of_toolbar_focus_area"
+ app:layout_constraintTop_toBottomOf="@id/top_part_of_toolbar_focus_area"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_toolbar_tab_item.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_toolbar_tab_item.xml
new file mode 100644
index 0000000..f71c4f0
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/layout/car_ui_portrait_toolbar_tab_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/car_portrait_ui_tab_width"
+ android:layout_height="@dimen/car_portrait_ui_tab_height"
+ android:paddingHorizontal="@dimen/car_ui_portrait_toolbar_tab_padding_horizontal"
+ android:paddingVertical="@dimen/car_ui_portrait_toolbar_tab_padding_vertical"
+ android:background="?android:attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/car_ui_portrait_toolbar_tab_item_icon"
+ android:layout_width="@dimen/car_ui_portrait_toolbar_tab_item_icon_size"
+ android:layout_height="@dimen/car_ui_portrait_toolbar_tab_item_icon_size"
+ android:layout_marginEnd="@dimen/car_ui_portrait_toolbar_tab_item_icon_margin"
+ android:scaleType="fitCenter"
+ android:tint="@color/car_ui_portrait_toolbar_tab_item_selector"
+ android:tintMode="src_in"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"/>
+
+ <TextView
+ android:id="@+id/car_ui_portrait_toolbar_tab_item_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.CarUi.Widget.Toolbar.Tab"
+ android:singleLine="true"
+ android:layout_toEndOf="@id/car_ui_portrait_toolbar_tab_item_icon"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/dimens.xml
new file mode 100644
index 0000000..8e81f48
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/dimens.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 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.
+ -->
+<resources>
+ <dimen name="car_ui_portrait_toolbar_logo_size">48dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_logo_margin">12dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_nav_icon_container_size">88dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_nav_icon_margin">20dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_nav_icon_size">48dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_padding">16dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_tab_item_icon_margin">24dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_tab_item_icon_size">36dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_tab_padding_horizontal">24dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_tab_padding_vertical">30dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_width">352dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_title_container_margin">12dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_title_logo_container_padding">24dp</dimen>
+ <dimen name="car_ui_portrait_toolbar_height">96dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/drawables.xml
similarity index 85%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/drawables.xml
index 8ad0860..bd5084e 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/drawables.xml
@@ -15,5 +15,6 @@
~ limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+ <!-- Toolbar background color -->
+ <drawable name="car_ui_portrait_toolbar_background">@color/car_background</drawable>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/themes.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/themes.xml
new file mode 100644
index 0000000..df6ba2d
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/res/values/themes.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<resources>
+ <style name="TextAppearance.CarUi.Widget" parent="android:TextAppearance.DeviceDefault.Widget">
+ <item name="android:textAlignment">viewStart</item>
+ </style>
+
+ <style name="TextAppearance.CarUi.Widget.Toolbar"/>
+
+ <style name="TextAppearance.CarUi.Widget.Toolbar.Title"
+ parent="TextAppearance.Car.Subhead.Large">
+ <item name="android:singleLine">true</item>
+ <item name="android:textColor">@color/car_on_surface</item>
+ <item name="android:ellipsize">none</item>
+ <item name="android:textAlignment">viewStart</item>
+ </style>
+
+ <style name="TextAppearance.CarUi.Widget.Toolbar.Tab" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/car_ui_portrait_toolbar_tab_item_selector</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textFontWeight">400</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/AndroidManifest.xml b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/AndroidManifest.xml
index 2428ea5..e048607 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/AndroidManifest.xml
@@ -25,12 +25,14 @@
<application
android:supportsRtl="true">
- <uses-library android:name="com.android.car.ui.sharedlibrary" android:required="true" />
+ <library android:name="com.chassis.car.ui.plugin" />
+ <!-- TODO(b/288620970): remove once all apps are migrated to use shared library directly-->
<provider
android:name="com.android.car.ui.plugin.PluginNameProvider"
android:authorities="com.android.car.ui.plugin"
+ android:directBootAware="true"
android:enabled="false"
- android:exported="false"
+ android:exported="true"
tools:ignore="MissingClass"/>
</application>
</manifest>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginContextWrapper.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginContextWrapper.java
index bd33c04..83c8ab7 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginContextWrapper.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginContextWrapper.java
@@ -18,18 +18,73 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* A wrapper class around the plugin context so it can provide {@link android.app.Application}
* context.
*/
public class PluginContextWrapper extends ContextWrapper {
- public PluginContextWrapper(Context base) {
- super(base);
+
+ @NonNull
+ private final String mApplicationPackageName;
+ @Nullable
+ private WindowManager mWindowManager;
+ @Nullable
+ private LayoutInflater mLayoutInflater = null;
+
+ public PluginContextWrapper(@NonNull Context pluginContext,
+ @NonNull String applicationPackageName) {
+ super(pluginContext);
+ mApplicationPackageName = applicationPackageName;
}
+ /**
+ * Return this plugin context as the application context so that it doesn't return null when
+ * called in the static implementation, for example, {@code MenuItem}
+ */
@Override
public Context getApplicationContext() {
return this;
}
+
+ /**
+ * Return the application package name instead of the plugin package name because
+ * {@code SearchResultsProvider} in static implementation needs application id for authority
+ */
+ @Override
+ public String getPackageName() {
+ return mApplicationPackageName;
+ }
+
+ /**
+ * Sets the {@link WindowManager} instance for the plugin {@link Context}.
+ * This was specifically needed for launching {@code Dialog}s.
+ * @param windowManager needs to be attached to the main Activity
+ */
+ public void setWindowManager(@Nullable WindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public Object getSystemService(@NonNull String name) {
+ if (WINDOW_SERVICE.equals(name) && mWindowManager != null) {
+ return mWindowManager;
+ }
+ // Return a layout inflater cloned with this context so that views inflated with a
+ // LayoutInflater.from call will return a PluginContextWrapper which is needed to override
+ // other methods in Context, e.g., getPackageName
+ if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+ if (mLayoutInflater == null) {
+ mLayoutInflater = ((LayoutInflater) super.getSystemService(LAYOUT_INFLATER_SERVICE))
+ .cloneInContext(this);
+ }
+ return mLayoutInflater;
+ }
+ return super.getSystemService(name);
+ }
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginUiContextFactory.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginUiContextFactory.java
new file mode 100644
index 0000000..193a44e
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginUiContextFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.ui.plugin;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+import com.chassis.car.ui.plugin.CarUiProxyLayoutInflaterFactory;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * This class maintains plugin ui contexts per app, which are used to inflate views with the plugin.
+ */
+public final class PluginUiContextFactory {
+ /** The singular context generated from the plugin package in {@code PluginFactorySingleton}. */
+ private final Context mPluginContext;
+ /** The most recently created/referenced plugin ui context. */
+ private WeakReference<Context> mRecentUiContext = null;
+ /** A map from app contexts to their corresponding plugin ui contexts. */
+ private Map<Context, Context> mAppToPluginContextMap = new WeakHashMap<>();
+
+ public PluginUiContextFactory(@NonNull Context pluginContext) {
+ mPluginContext = pluginContext;
+ }
+
+ /**
+ * Returns the most recently referenced plugin ui context from {@code getPluginUiContext}. This
+ * is used by PluginFactoryImplV# to obtain a relevant ui context without a source context
+ * (i.e., for createListItemAdapter which does not receive a context as a parameter).
+ *
+ * Note: list items are always used with a RecyclerView, so mRecentUiContext will be set in
+ * createRecyclerView method, which should happen before createListItemAdapter.
+ *
+ * @throws IllegalStateException if mRecentUiContext is not initialized
+ */
+ @NonNull
+ public Context getRecentPluginUiContext() throws IllegalStateException {
+ if (mRecentUiContext == null) {
+ throw new IllegalStateException(
+ "Method getRecentPluginUiContext cannot be called before getPluginUiContext");
+ }
+ return mRecentUiContext.get();
+ }
+
+ /**
+ * This method tries to return a ui context for usage in the plugin that has the same
+ * configuration as the given source ui context.
+ *
+ * @param sourceContext A ui context, normally an Activity context.
+ */
+ @NonNull
+ public Context getPluginUiContext(@NonNull Context sourceContext) {
+ Context uiContext = mAppToPluginContextMap.get(sourceContext);
+
+ if (uiContext == null) {
+ uiContext = mPluginContext;
+ if (!uiContext.isUiContext()) {
+ uiContext = uiContext
+ .createWindowContext(sourceContext.getDisplay(), TYPE_APPLICATION, null);
+ }
+ }
+
+ Configuration currentConfiguration = uiContext.getResources().getConfiguration();
+ Configuration newConfiguration = sourceContext.getResources().getConfiguration();
+ if (currentConfiguration.diff(newConfiguration) != 0) {
+ uiContext = uiContext.createConfigurationContext(newConfiguration);
+ }
+
+ // Only wrap uiContext the first time it's configured
+ if (!(uiContext instanceof PluginContextWrapper)) {
+ uiContext = new PluginContextWrapper(uiContext, sourceContext.getPackageName());
+ ((PluginContextWrapper) uiContext).setWindowManager((WindowManager) sourceContext
+ .getSystemService(Context.WINDOW_SERVICE));
+ }
+
+ // Add a custom layout inflater that can handle things like CarUiTextView that is in the
+ // layout files of the car-ui-lib static implementation
+ LayoutInflater inflater = LayoutInflater.from(uiContext);
+ if (inflater.getFactory2() == null) {
+ inflater.setFactory2(new CarUiProxyLayoutInflaterFactory());
+ }
+
+ mAppToPluginContextMap.put(sourceContext, uiContext);
+ mRecentUiContext = new WeakReference<Context>(uiContext);
+
+ return uiContext;
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginVersionProviderImpl.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginVersionProviderImpl.java
index 9379f11..5562c60 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginVersionProviderImpl.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/android/car/ui/plugin/PluginVersionProviderImpl.java
@@ -21,9 +21,14 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
+
import com.android.car.ui.plugin.oemapis.PluginVersionProviderOEMV1;
-import com.chassis.car.ui.plugin.PluginFactoryImpl;
+import com.chassis.car.ui.plugin.PluginFactoryImplV2;
+import com.chassis.car.ui.plugin.PluginFactoryImplV5;
+import com.chassis.car.ui.plugin.PluginFactoryImplV6;
+import com.chassis.car.ui.plugin.PluginFactoryImplV7;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -31,22 +36,26 @@
import java.util.List;
/**
- * Builds a Plugin that will delegate to the standard car-ui-lib implementation. The main benefit
+ * Builds a plugin that will delegate to the standard car-ui-lib implementation. The main benefit
* of this is so that customizations can be applied to the car-ui-lib via a RRO without the need to
* target each app specifically. Note: it only applies to the components that come through the
* plugin system.
*/
public class PluginVersionProviderImpl implements PluginVersionProviderOEMV1 {
public static final String TAG = "PluginVersionProvider";
- public static final String SHARED_LIBRARY_PACKAGE = "com.android.car.ui.sharedlibrary";
private static final List<String> DENIED_PACKAGES = new ArrayList<>(List.of(
// TODO(b/260267959) remove.
"com.android.vending"
));
+ /**
+ * This method returns different implementations of {@code PluginFactoryOEMV#} depending on the
+ * max version supported by an app (i.e., if an app uses an older version of car-ui-lib).
+ */
@Override
- public Object getPluginFactory(int maxVersion, Context context, String packageName) {
+ public Object getPluginFactory(
+ int maxVersion, @NonNull Context context, @NonNull String packageName) {
if (DENIED_PACKAGES.contains(packageName)) {
return null;
@@ -59,15 +68,36 @@
if (id == 0x01 || id == 0x7f) {
continue;
}
- if (SHARED_LIBRARY_PACKAGE.equals(r.valueAt(i))) {
+ if (context.getPackageName().equals(r.valueAt(i))) {
Log.d(TAG, "PluginVersionProviderImpl : getPluginFactory: rewriting R prefix"
- + " values for " + SHARED_LIBRARY_PACKAGE + " to: "
+ + " values for " + context.getPackageName() + " to: "
+ Integer.toHexString(id));
rewriteRValues(context.getClassLoader(), r.valueAt(i), id);
}
}
- return new PluginFactoryImpl(new PluginContextWrapper(context));
+ Context pluginContext = new PluginContextWrapper(context, packageName);
+ switch (maxVersion) {
+ // There was a bug in car-ui-lib which only passed 1 as the max supported version of the
+ // car-ui-lib plugin, even when there were more versions supported (e.g., V2, V3, V4).
+ // This was eventually fixed when support for V5 was added. The guidance for oems is to
+ // return a PluginFactoryV2 whenever the max version is less than 5 because apps only
+ // supporting PluginFactoryV1 will never be deployed on system supporting car-ui-lib
+ // plugin. Furthermore, system apps developed on sc-car or newer branches will support
+ // PluginFactoryV2 or newer.
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return new PluginFactoryImplV2(pluginContext);
+ case 5:
+ return new PluginFactoryImplV5(pluginContext);
+ case 6:
+ return new PluginFactoryImplV6(pluginContext);
+ // Keep the newest version as default case and add old versions above
+ default:
+ return new PluginFactoryImplV7(pluginContext);
+ }
}
/**
@@ -84,6 +114,7 @@
return (SparseArray<String>) invoke;
} catch (NoSuchMethodException e) {
// No rewriting to be done.
+ Log.e(TAG, "getAssignedPackageIdentifiers method not found");
return new SparseArray<>();
} catch (IllegalAccessException e) {
cause = e;
@@ -114,9 +145,11 @@
} catch (ClassNotFoundException e) {
// This is not necessarily an error, as some packages do not ship with resources
// (or they do not need rewriting).
+ Log.e(TAG, "R class not found for package " + packageName);
return;
} catch (NoSuchMethodException e) {
// No rewriting to be done.
+ Log.e(TAG, "onResourcesLoaded method not found for package " + packageName);
return;
} catch (IllegalAccessException e) {
cause = e;
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/CarUiProxyLayoutInflaterFactory.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/CarUiProxyLayoutInflaterFactory.java
index d043bfa..2d9cb33 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/CarUiProxyLayoutInflaterFactory.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/CarUiProxyLayoutInflaterFactory.java
@@ -39,7 +39,6 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatViewInflater;
-import com.android.car.ui.R;
import com.android.car.ui.pluginsupport.PluginFactoryStub;
import com.android.car.ui.preference.CarUiPreferenceViewStub;
import com.android.car.ui.recyclerview.CarUiRecyclerView;
@@ -84,9 +83,7 @@
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.Preference, 0, 0);
- int preferenceType = a.getInt(
- com.android.car.ui.sharedlibrary.R.styleable.Preference_carUiPreferenceType,
- PREFERENCE);
+ int preferenceType = a.getInt(R.styleable.Preference_carUiPreferenceType, PREFERENCE);
a.recycle();
return preferenceType;
}
@@ -95,41 +92,29 @@
@CarUiPreferenceViewStub.PreferenceType int preferenceType) {
switch (preferenceType) {
case SWITCH:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_primary_switch_internal;
+ return R.layout.car_ui_preference_primary_switch_internal;
case EDIT_TEXT:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_dialog_edittext_internal;
+ return R.layout.car_ui_preference_dialog_edittext_internal;
case CATEGORY:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_category_internal;
+ return R.layout.car_ui_preference_category_internal;
case DROPDOWN:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_dropdown_internal;
+ return R.layout.car_ui_preference_dropdown_internal;
case TWO_ACTION:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_two_action_preference_internal;
+ return R.layout.car_ui_two_action_preference_internal;
case TWO_ACTION_TEXT:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_two_action_text_internal;
+ return R.layout.car_ui_preference_two_action_text_internal;
case TWO_ACTION_TEXT_BORDERLESS:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_two_action_text_borderless_internal;
+ return R.layout.car_ui_preference_two_action_text_borderless_internal;
case TWO_ACTION_ICON:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_two_action_icon_internal;
+ return R.layout.car_ui_preference_two_action_icon_internal;
case TWO_ACTION_SWITCH:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_two_action_switch_internal;
+ return R.layout.car_ui_preference_two_action_switch_internal;
case SEEKBAR_DIALOG:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_seekbar_dialog_internal;
+ return R.layout.car_ui_seekbar_dialog_internal;
case FOOTER:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_footer_internal;
+ return R.layout.car_ui_preference_footer_internal;
default:
- return com.android.car.ui.sharedlibrary
- .R.layout.car_ui_preference_internal;
+ return R.layout.car_ui_preference_internal;
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImpl.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImpl.java
deleted file mode 100644
index ade8939..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImpl.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.chassis.car.ui.plugin;
-
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build.VERSION;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.car.ui.CarUiText;
-import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
-import com.android.car.ui.plugin.PluginContextWrapper;
-import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
-import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
-import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
-import com.android.car.ui.plugin.oemapis.PluginFactoryOEMV6;
-import com.android.car.ui.plugin.oemapis.TextOEMV1;
-import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV3;
-import com.android.car.ui.plugin.oemapis.preference.PreferenceOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.ContentListItemOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.HeaderListItemOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2;
-import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
-import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
-import com.android.car.ui.recyclerview.CarUiContentListItem;
-import com.android.car.ui.recyclerview.CarUiHeaderListItem;
-import com.android.car.ui.recyclerview.CarUiListItem;
-import com.android.car.ui.recyclerview.CarUiListItemAdapter;
-import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
-import com.android.car.ui.toolbar.ToolbarControllerImpl;
-import com.android.car.ui.utils.CarUiUtils;
-
-import com.chassis.car.ui.plugin.appstyledview.AppStyledViewControllerAdapterProxy;
-import com.chassis.car.ui.plugin.preference.PreferenceAdapterProxy;
-import com.chassis.car.ui.plugin.recyclerview.CarListItemAdapterAdapterProxy;
-import com.chassis.car.ui.plugin.recyclerview.RecyclerViewAdapterProxy;
-import com.chassis.car.ui.plugin.toolbar.ToolbarAdapterProxy;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * An implication of the plugin factory that delegates back to the car-ui-lib implementation.
- * The main benefit of this is so that customizations can be applied to the car-ui-lib via a RRO
- * without the need to target each app specifically. Note: it only applies to the components that
- * come through the plugin system.
- */
-public class PluginFactoryImpl implements PluginFactoryOEMV6 {
-
- private final Context mPluginContext;
- Map<Context, Context> mAppToPluginContextMap = new WeakHashMap<>();
-
- public PluginFactoryImpl(Context pluginContext) {
- mPluginContext = pluginContext;
- }
-
- @Override
- public void setRotaryFactories(
- com.android.car.ui.plugin.oemapis.Function<Context, FocusParkingViewOEMV1> function,
- com.android.car.ui.plugin.oemapis.Function<Context, FocusAreaOEMV1> function1) {
- }
-
- @Nullable
- @Override
- public ToolbarControllerOEMV2 installBaseLayoutAround(@NonNull Context context,
- @NonNull View view,
- @Nullable com.android.car.ui.plugin.oemapis.Consumer<InsetsOEMV1> consumer,
- boolean b,
- boolean b1) {
- Context pluginContext = getPluginUiContext(context, mPluginContext);
- ToolbarControllerImpl toolbarController = new ToolbarControllerImpl(pluginContext, view);
- return new ToolbarAdapterProxy(pluginContext, toolbarController);
- }
-
- @Override
- public boolean customizesBaseLayout() {
- return false;
- }
-
- @Override
- public PreferenceOEMV1 createCarUiPreference(@NonNull Context sourceContext) {
- Context pluginContext = getPluginUiContext(sourceContext, mPluginContext);
- return new PreferenceAdapterProxy(pluginContext, sourceContext);
- }
-
- @Nullable
- @Override
- public AppStyledViewControllerOEMV3 createAppStyledView(@NonNull Context context) {
- Context pluginContext = getPluginUiContext(context, mPluginContext);
- // build the app styled controller that will be delegated to
- AppStyledViewControllerImpl appStyledViewController = new AppStyledViewControllerImpl(
- pluginContext);
- return new AppStyledViewControllerAdapterProxy(appStyledViewController);
- }
-
- @Nullable
- @Override
- public RecyclerViewOEMV2 createRecyclerView(@NonNull Context context,
- @Nullable RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
- Context pluginContext = getPluginUiContext(context, mPluginContext);
- CarUiRecyclerViewImpl recyclerView =
- new CarUiRecyclerViewImpl(pluginContext, recyclerViewAttributesOEMV1);
- return new RecyclerViewAdapterProxy(pluginContext, recyclerView,
- recyclerViewAttributesOEMV1);
- }
-
- @Override
- public AdapterOEMV1<? extends ViewHolderOEMV1> createListItemAdapter(
- List<ListItemOEMV1> items) {
- // TODO: add this here? Context pluginContext = getPluginUiContext(context, mPluginContext);
- List<? extends CarUiListItem> staticItems = CarUiUtils.convertList(items,
- PluginFactoryImpl::toStaticListItem);
- // Build the CarUiListItemAdapter that will be delegated to
- CarUiListItemAdapter carUiListItemAdapter = new CarUiListItemAdapter(staticItems);
- return new CarListItemAdapterAdapterProxy(carUiListItemAdapter, mPluginContext);
- }
-
- /**
- * The plugin was passed the list items as {@link ListItemOEMV1}s and thus must be converted
- * back to use the "original" {@link CarUiListItem}s that's expected by the
- * {@link CarUiListItemAdapter}.
- */
- private static CarUiListItem toStaticListItem(ListItemOEMV1 item) {
- if (item instanceof HeaderListItemOEMV1) {
- HeaderListItemOEMV1 header = (HeaderListItemOEMV1) item;
- return new CarUiHeaderListItem(header.getTitle(), header.getBody());
- } else if (item instanceof ContentListItemOEMV1) {
- ContentListItemOEMV1 contentItem = (ContentListItemOEMV1) item;
-
- CarUiContentListItem listItem = new CarUiContentListItem(
- toCarUiContentListItemAction(contentItem.getAction()));
-
- if (contentItem.getTitle() != null) {
- listItem.setTitle(toCarUiText(contentItem.getTitle()));
- }
-
- if (contentItem.getBody() != null) {
- listItem.setBody(toCarUiText(contentItem.getBody()));
- }
-
- listItem.setIcon(contentItem.getIcon());
- listItem.setPrimaryIconType(
- toCarUiConteentListItemIconType(contentItem.getPrimaryIconType()));
-
- if (contentItem.getAction() == ContentListItemOEMV1.Action.ICON) {
- CarUiContentListItem.OnClickListener listener =
- contentItem.getSupplementalIconOnClickListener() != null
- ? carUiContentListItem ->
- contentItem.getSupplementalIconOnClickListener().accept(
- contentItem) : null;
-
-
- listItem.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
- }
-
- if (contentItem.getOnClickListener() != null) {
- CarUiContentListItem.OnClickListener listener =
- contentItem.getOnClickListener() != null
- ? carUiContentListItem ->
- contentItem.getOnClickListener().accept(
- contentItem) : null;
- listItem.setOnItemClickedListener(listener);
- }
-
- listItem.setOnCheckedChangeListener((carUiContentListItem, checked) ->
- carUiContentListItem.setChecked(checked));
- listItem.setActionDividerVisible(contentItem.isActionDividerVisible());
- listItem.setEnabled(contentItem.isEnabled());
- listItem.setChecked(contentItem.isChecked());
- listItem.setActivated(contentItem.isActivated());
- listItem.setSecure(contentItem.isSecure());
- return listItem;
- } else {
- throw new IllegalStateException("Unknown view type.");
- }
- }
-
- private static CarUiText toCarUiText(TextOEMV1 text) {
- return new CarUiText.Builder(text.getTextVariants()).setMaxChars(
- text.getMaxChars()).setMaxLines(text.getMaxLines()).build();
- }
-
- private static List<CarUiText> toCarUiText(List<TextOEMV1> lines) {
- List<CarUiText> oemLines = new ArrayList<>();
-
- for (TextOEMV1 line : lines) {
- oemLines.add(new CarUiText.Builder(line.getTextVariants()).setMaxChars(
- line.getMaxChars()).setMaxLines(line.getMaxLines()).build());
- }
- return oemLines;
- }
-
- private static CarUiContentListItem.Action toCarUiContentListItemAction(
- ContentListItemOEMV1.Action action) {
- switch (action) {
- case NONE:
- return CarUiContentListItem.Action.NONE;
- case SWITCH:
- return CarUiContentListItem.Action.SWITCH;
- case CHECK_BOX:
- return CarUiContentListItem.Action.CHECK_BOX;
- case RADIO_BUTTON:
- return CarUiContentListItem.Action.RADIO_BUTTON;
- case ICON:
- return CarUiContentListItem.Action.ICON;
- case CHEVRON:
- return CarUiContentListItem.Action.CHEVRON;
- default:
- throw new IllegalStateException("Unexpected list item action type");
- }
- }
-
- private static CarUiContentListItem.IconType toCarUiConteentListItemIconType(
- ContentListItemOEMV1.IconType iconType) {
- switch (iconType) {
- case CONTENT:
- return CarUiContentListItem.IconType.CONTENT;
- case STANDARD:
- return CarUiContentListItem.IconType.STANDARD;
- case AVATAR:
- return CarUiContentListItem.IconType.AVATAR;
- default:
- throw new IllegalStateException("Unexpected list item icon type");
- }
- }
-
- /**
- * This method tries to return a ui-context for usage in the plugin that has the same
- * configuration as the given source ui context.
- *
- * @param sourceContext a UI context, normally an Activity context.
- */
- private Context getPluginUiContext(@NonNull Context sourceContext,
- @NonNull Context pluginContext) {
-
- Context uiContext = mAppToPluginContextMap.get(sourceContext);
-
- if (uiContext == null) {
- uiContext = pluginContext;
- if (VERSION.SDK_INT >= 34 /* Android U */ && !uiContext.isUiContext()) {
- // On U and above we need a UiContext for initializing the proxy plugin.
- uiContext = pluginContext
- .createWindowContext(sourceContext.getDisplay(), TYPE_APPLICATION, null);
- }
- }
-
- Configuration currentConfiguration = uiContext.getResources()
- .getConfiguration();
- Configuration newConfiguration = sourceContext.getResources().getConfiguration();
- if (currentConfiguration.diff(newConfiguration) != 0) {
- uiContext = uiContext.createConfigurationContext(newConfiguration);
- }
-
- uiContext = new PluginContextWrapper(uiContext);
-
- // add a custom layout inflater that can handle things like CarUiTextView that is in the
- // layout files of the car-ui-lib static implementation
- LayoutInflater inflater = LayoutInflater.from(uiContext);
- if (inflater.getFactory2() == null) {
- inflater.setFactory2(new CarUiProxyLayoutInflaterFactory());
- }
-
- mAppToPluginContextMap.put(sourceContext, uiContext);
-
- return uiContext;
- }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV2.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV2.java
new file mode 100644
index 0000000..d9e93f7
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV2.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
+import com.android.car.ui.plugin.PluginUiContextFactory;
+import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
+import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
+import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
+import com.android.car.ui.plugin.oemapis.PluginFactoryOEMV2;
+import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV1;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
+import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
+import com.android.car.ui.utils.CarUiUtils;
+
+
+import com.chassis.car.ui.plugin.appstyledview.AppStyledViewControllerAdapterProxyV1;
+import com.chassis.car.ui.plugin.recyclerview.CarListItemAdapterAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.ListItemUtils;
+import com.chassis.car.ui.plugin.recyclerview.RecyclerViewAdapterProxyV1;
+import com.chassis.car.ui.plugin.toolbar.BaseLayoutInstaller;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * See {@code PluginFactoryImplV7}. This class is for backwards compatibility with apps that use
+ * an older version of car-ui-lib.
+ */
+public class PluginFactoryImplV2 implements PluginFactoryOEMV2 {
+ private final PluginUiContextFactory mPluginUiContextFactory;
+ @Nullable
+ private Function<Context, FocusParkingViewOEMV1> mFocusParkingViewFactory;
+ @Nullable
+ private Function<Context, FocusAreaOEMV1> mFocusAreaFactory;
+
+ public PluginFactoryImplV2(Context pluginContext) {
+ mPluginUiContextFactory = new PluginUiContextFactory(pluginContext);
+ }
+
+ @Override
+ public void setRotaryFactories(
+ Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ mFocusParkingViewFactory = focusParkingViewFactory;
+ mFocusAreaFactory = focusAreaFactory;
+ }
+
+ @Nullable
+ @Override
+ public ToolbarControllerOEMV1 installBaseLayoutAround(
+ @NonNull Context sourceContext,
+ @NonNull View contentView,
+ @Nullable Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return BaseLayoutInstaller.installBaseLayoutAroundV1(
+ pluginContext,
+ contentView,
+ insetsChangedListener,
+ toolbarEnabled,
+ fullscreen,
+ mFocusParkingViewFactory,
+ mFocusAreaFactory);
+ }
+
+ @Override
+ public boolean customizesBaseLayout() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AppStyledViewControllerOEMV1 createAppStyledView(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ // build the app styled controller that will be delegated to
+ AppStyledViewControllerImpl appStyledViewController = new AppStyledViewControllerImpl(
+ pluginContext);
+ return new AppStyledViewControllerAdapterProxyV1(appStyledViewController);
+ }
+
+ @Nullable
+ @Override
+ public RecyclerViewOEMV1 createRecyclerView(@NonNull Context sourceContext,
+ @Nullable RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ CarUiRecyclerViewImpl recyclerView =
+ new CarUiRecyclerViewImpl(pluginContext, recyclerViewAttributesOEMV1);
+ return new RecyclerViewAdapterProxyV1(pluginContext, recyclerView,
+ recyclerViewAttributesOEMV1);
+ }
+
+ @Override
+ public AdapterOEMV1<? extends ViewHolderOEMV1> createListItemAdapter(
+ List<ListItemOEMV1> items) {
+ List<? extends CarUiListItem> staticItems = CarUiUtils.convertList(items,
+ ListItemUtils::toStaticListItem);
+ // Build the CarUiListItemAdapter that will be delegated to
+ CarUiListItemAdapter carUiListItemAdapter = new CarUiListItemAdapter(staticItems);
+ return new CarListItemAdapterAdapterProxy(
+ carUiListItemAdapter, mPluginUiContextFactory.getRecentPluginUiContext());
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV5.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV5.java
new file mode 100644
index 0000000..4ce5796
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV5.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
+import com.android.car.ui.plugin.PluginUiContextFactory;
+import com.android.car.ui.plugin.oemapis.Consumer;
+import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
+import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
+import com.android.car.ui.plugin.oemapis.Function;
+import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
+import com.android.car.ui.plugin.oemapis.PluginFactoryOEMV5;
+import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV3;
+import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2;
+import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
+import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
+import com.android.car.ui.utils.CarUiUtils;
+
+
+import com.chassis.car.ui.plugin.appstyledview.AppStyledViewControllerAdapterProxyV3;
+import com.chassis.car.ui.plugin.recyclerview.CarListItemAdapterAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.ListItemUtils;
+import com.chassis.car.ui.plugin.recyclerview.RecyclerViewAdapterProxyV2;
+import com.chassis.car.ui.plugin.toolbar.BaseLayoutInstaller;
+
+import java.util.List;
+
+/**
+ * See {@code PluginFactoryImplV7}. This class is for backwards compatibility with apps that use
+ * an older version of car-ui-lib.
+ */
+public class PluginFactoryImplV5 implements PluginFactoryOEMV5 {
+ private final PluginUiContextFactory mPluginUiContextFactory;
+ @Nullable
+ private Function<Context, FocusParkingViewOEMV1> mFocusParkingViewFactory;
+ @Nullable
+ private Function<Context, FocusAreaOEMV1> mFocusAreaFactory;
+
+
+ public PluginFactoryImplV5(Context pluginContext) {
+ mPluginUiContextFactory = new PluginUiContextFactory(pluginContext);
+ }
+ @Override
+ public void setRotaryFactories(
+ Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ mFocusParkingViewFactory = focusParkingViewFactory;
+ mFocusAreaFactory = focusAreaFactory;
+ }
+
+ @Nullable
+ @Override
+ public ToolbarControllerOEMV2 installBaseLayoutAround(
+ @NonNull Context sourceContext,
+ @NonNull View contentView,
+ Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return BaseLayoutInstaller.installBaseLayoutAroundV2(
+ pluginContext,
+ contentView,
+ insetsChangedListener,
+ toolbarEnabled,
+ fullscreen,
+ mFocusParkingViewFactory,
+ mFocusAreaFactory);
+ }
+
+ @Override
+ public boolean customizesBaseLayout() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AppStyledViewControllerOEMV3 createAppStyledView(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ // build the app styled controller that will be delegated to
+ AppStyledViewControllerImpl appStyledViewController = new AppStyledViewControllerImpl(
+ pluginContext);
+ return new AppStyledViewControllerAdapterProxyV3(appStyledViewController);
+ }
+
+ @Nullable
+ @Override
+ public RecyclerViewOEMV2 createRecyclerView(@NonNull Context sourceContext,
+ @Nullable RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ CarUiRecyclerViewImpl recyclerView =
+ new CarUiRecyclerViewImpl(pluginContext, recyclerViewAttributesOEMV1);
+ return new RecyclerViewAdapterProxyV2(pluginContext, recyclerView,
+ recyclerViewAttributesOEMV1);
+ }
+
+ @Override
+ public AdapterOEMV1<? extends ViewHolderOEMV1> createListItemAdapter(
+ List<ListItemOEMV1> items) {
+ List<? extends CarUiListItem> staticItems = CarUiUtils.convertList(items,
+ ListItemUtils::toStaticListItem);
+ // Build the CarUiListItemAdapter that will be delegated to
+ CarUiListItemAdapter carUiListItemAdapter = new CarUiListItemAdapter(staticItems);
+ return new CarListItemAdapterAdapterProxy(
+ carUiListItemAdapter, mPluginUiContextFactory.getRecentPluginUiContext());
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV6.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV6.java
new file mode 100644
index 0000000..2429d0e
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV6.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
+import com.android.car.ui.plugin.PluginUiContextFactory;
+import com.android.car.ui.plugin.oemapis.Consumer;
+import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
+import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
+import com.android.car.ui.plugin.oemapis.Function;
+import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
+import com.android.car.ui.plugin.oemapis.PluginFactoryOEMV6;
+import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV3;
+import com.android.car.ui.plugin.oemapis.preference.PreferenceOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2;
+import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
+import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
+import com.android.car.ui.utils.CarUiUtils;
+
+import com.chassis.car.ui.plugin.appstyledview.AppStyledViewControllerAdapterProxyV3;
+import com.chassis.car.ui.plugin.preference.PreferenceAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.CarListItemAdapterAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.ListItemUtils;
+import com.chassis.car.ui.plugin.recyclerview.RecyclerViewAdapterProxyV2;
+import com.chassis.car.ui.plugin.toolbar.BaseLayoutInstaller;
+
+import java.util.List;
+
+/**
+ * See {@code PluginFactoryImplV7}. This class is for backwards compatibility with apps that use
+ * an older version of car-ui-lib.
+ */
+public class PluginFactoryImplV6 implements PluginFactoryOEMV6 {
+
+ private final PluginUiContextFactory mPluginUiContextFactory;
+ @Nullable
+ private Function<Context, FocusParkingViewOEMV1> mFocusParkingViewFactory;
+ @Nullable
+ private Function<Context, FocusAreaOEMV1> mFocusAreaFactory;
+
+ public PluginFactoryImplV6(Context pluginContext) {
+ mPluginUiContextFactory = new PluginUiContextFactory(pluginContext);
+ }
+
+ @Override
+ public void setRotaryFactories(
+ Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ mFocusParkingViewFactory = focusParkingViewFactory;
+ mFocusAreaFactory = focusAreaFactory;
+ }
+
+ @Nullable
+ @Override
+ public ToolbarControllerOEMV2 installBaseLayoutAround(
+ @NonNull Context sourceContext,
+ @NonNull View contentView,
+ Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return BaseLayoutInstaller.installBaseLayoutAroundV2(
+ pluginContext,
+ contentView,
+ insetsChangedListener,
+ toolbarEnabled,
+ fullscreen,
+ mFocusParkingViewFactory,
+ mFocusAreaFactory);
+ }
+
+ @Override
+ public boolean customizesBaseLayout() {
+ return true;
+ }
+
+ @Override
+ public PreferenceOEMV1 createCarUiPreference(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return new PreferenceAdapterProxy(pluginContext, sourceContext);
+ }
+
+ @Nullable
+ @Override
+ public AppStyledViewControllerOEMV3 createAppStyledView(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ // build the app styled controller that will be delegated to
+ AppStyledViewControllerImpl appStyledViewController = new AppStyledViewControllerImpl(
+ pluginContext);
+ return new AppStyledViewControllerAdapterProxyV3(appStyledViewController);
+ }
+
+ @Nullable
+ @Override
+ public RecyclerViewOEMV2 createRecyclerView(@NonNull Context sourceContext,
+ @Nullable RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ CarUiRecyclerViewImpl recyclerView =
+ new CarUiRecyclerViewImpl(pluginContext, recyclerViewAttributesOEMV1);
+ return new RecyclerViewAdapterProxyV2(pluginContext, recyclerView,
+ recyclerViewAttributesOEMV1);
+ }
+
+ @Override
+ public AdapterOEMV1<? extends ViewHolderOEMV1> createListItemAdapter(
+ List<ListItemOEMV1> items) {
+ List<? extends CarUiListItem> staticItems = CarUiUtils.convertList(items,
+ ListItemUtils::toStaticListItem);
+ // Build the CarUiListItemAdapter that will be delegated to
+ CarUiListItemAdapter carUiListItemAdapter = new CarUiListItemAdapter(staticItems);
+ return new CarListItemAdapterAdapterProxy(
+ carUiListItemAdapter, mPluginUiContextFactory.getRecentPluginUiContext());
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV7.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV7.java
new file mode 100644
index 0000000..ccef43c
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/PluginFactoryImplV7.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
+import com.android.car.ui.plugin.PluginUiContextFactory;
+import com.android.car.ui.plugin.oemapis.Consumer;
+import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
+import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
+import com.android.car.ui.plugin.oemapis.Function;
+import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
+import com.android.car.ui.plugin.oemapis.PluginFactoryOEMV7;
+import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV3;
+import com.android.car.ui.plugin.oemapis.preference.PreferenceOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2;
+import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV3;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
+import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
+import com.android.car.ui.utils.CarUiUtils;
+
+import com.chassis.car.ui.plugin.appstyledview.AppStyledViewControllerAdapterProxyV3;
+import com.chassis.car.ui.plugin.preference.PreferenceAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.CarListItemAdapterAdapterProxy;
+import com.chassis.car.ui.plugin.recyclerview.ListItemUtils;
+import com.chassis.car.ui.plugin.recyclerview.RecyclerViewAdapterProxyV2;
+import com.chassis.car.ui.plugin.toolbar.BaseLayoutInstaller;
+
+import java.util.List;
+
+/**
+ * See {@code PluginFactoryImplV7}. This class is for backwards compatibility with apps that use
+ * an older version of car-ui-lib.
+ */
+public class PluginFactoryImplV7 implements PluginFactoryOEMV7 {
+
+ private final PluginUiContextFactory mPluginUiContextFactory;
+ @Nullable
+ private Function<Context, FocusParkingViewOEMV1> mFocusParkingViewFactory;
+ @Nullable
+ private Function<Context, FocusAreaOEMV1> mFocusAreaFactory;
+
+ public PluginFactoryImplV7(Context pluginContext) {
+ mPluginUiContextFactory = new PluginUiContextFactory(pluginContext);
+ }
+
+ @Override
+ public void setRotaryFactories(
+ Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ mFocusParkingViewFactory = focusParkingViewFactory;
+ mFocusAreaFactory = focusAreaFactory;
+ }
+
+ @Nullable
+ @Override
+ public ToolbarControllerOEMV3 installBaseLayoutAround(
+ @NonNull Context sourceContext,
+ @NonNull View contentView,
+ Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return BaseLayoutInstaller.installBaseLayoutAroundV3(
+ pluginContext,
+ contentView,
+ insetsChangedListener,
+ toolbarEnabled,
+ fullscreen,
+ mFocusParkingViewFactory,
+ mFocusAreaFactory);
+ }
+
+ @Override
+ public boolean customizesBaseLayout() {
+ return true;
+ }
+
+ @Override
+ public PreferenceOEMV1 createCarUiPreference(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ return new PreferenceAdapterProxy(pluginContext, sourceContext);
+ }
+
+ @Nullable
+ @Override
+ public AppStyledViewControllerOEMV3 createAppStyledView(@NonNull Context sourceContext) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ // build the app styled controller that will be delegated to
+ AppStyledViewControllerImpl appStyledViewController = new AppStyledViewControllerImpl(
+ pluginContext);
+ return new AppStyledViewControllerAdapterProxyV3(appStyledViewController);
+ }
+
+ @Nullable
+ @Override
+ public RecyclerViewOEMV2 createRecyclerView(@NonNull Context sourceContext,
+ @Nullable RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
+ Context pluginContext = mPluginUiContextFactory.getPluginUiContext(sourceContext);
+ CarUiRecyclerViewImpl recyclerView =
+ new CarUiRecyclerViewImpl(pluginContext, recyclerViewAttributesOEMV1);
+ return new RecyclerViewAdapterProxyV2(pluginContext, recyclerView,
+ recyclerViewAttributesOEMV1);
+ }
+
+ @Override
+ public AdapterOEMV1<? extends ViewHolderOEMV1> createListItemAdapter(
+ List<ListItemOEMV1> items) {
+ List<? extends CarUiListItem> staticItems = CarUiUtils.convertList(items,
+ ListItemUtils::toStaticListItem);
+ // Build the CarUiListItemAdapter that will be delegated to
+ CarUiListItemAdapter carUiListItemAdapter = new CarUiListItemAdapter(staticItems);
+ return new CarListItemAdapterAdapterProxy(
+ carUiListItemAdapter, mPluginUiContextFactory.getRecentPluginUiContext());
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV1.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV1.java
new file mode 100644
index 0000000..d8a8529
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV1.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.appstyledview;
+
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.NonNull;
+
+import com.android.car.ui.appstyledview.AppStyledDialogController.NavIcon;
+import com.android.car.ui.appstyledview.AppStyledViewController;
+import com.android.car.ui.plugin.oemapis.appstyledview.AppStyledViewControllerOEMV1;
+
+/**
+ * See {@code AppStyledViewControllerAdapterProxyV3}. This class is for backwards compatibility with
+ * apps that use an older version of car-ui-lib.
+ */
+public class AppStyledViewControllerAdapterProxyV1 implements AppStyledViewControllerOEMV1 {
+
+ @NonNull
+ private final AppStyledViewController mStaticController;
+ private View mContentView;
+
+ public AppStyledViewControllerAdapterProxyV1(@NonNull AppStyledViewController controller) {
+ mStaticController = controller;
+ }
+
+ @Override
+ public View getView() {
+ return mStaticController.getAppStyledView(mContentView);
+ }
+
+ @Override
+ public void setContent(View view) {
+ mContentView = view;
+ }
+
+ @Override
+ public void setOnBackClickListener(Runnable runnable) {
+ mStaticController.setOnNavIconClickListener(runnable);
+ }
+
+ @Override
+ public void setNavIcon(@NavIcon int navIcon) {
+ switch (navIcon) {
+ case AppStyledViewControllerOEMV1.NAV_ICON_BACK:
+ mStaticController.setNavIcon(NavIcon.BACK);
+ break;
+ case AppStyledViewControllerOEMV1.NAV_ICON_CLOSE:
+ mStaticController.setNavIcon(NavIcon.CLOSE);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown nav icon style: " + navIcon);
+ }
+ }
+
+ @Override
+ public LayoutParams getDialogWindowLayoutParam(LayoutParams params) {
+ return mStaticController.getDialogWindowLayoutParam(params);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV3.java
similarity index 93%
rename from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxy.java
rename to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV3.java
index cb67591..f548469 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/appstyledview/AppStyledViewControllerAdapterProxyV3.java
@@ -29,14 +29,14 @@
/**
* Adapts a {@link AppStyledViewController} into a {@link AppStyledViewControllerOEMV3}.
*/
-public class AppStyledViewControllerAdapterProxy implements AppStyledViewControllerOEMV3 {
+public class AppStyledViewControllerAdapterProxyV3 implements AppStyledViewControllerOEMV3 {
@NonNull
private final AppStyledViewController mStaticController;
private View mContentView;
- public AppStyledViewControllerAdapterProxy(@NonNull AppStyledViewController controllerOEMV3) {
- mStaticController = controllerOEMV3;
+ public AppStyledViewControllerAdapterProxyV3(@NonNull AppStyledViewController controller) {
+ mStaticController = controller;
}
@Override
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/preference/PreferenceAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/preference/PreferenceAdapterProxy.java
index d97a442..6352528 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/preference/PreferenceAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/preference/PreferenceAdapterProxy.java
@@ -30,15 +30,18 @@
import static com.android.car.ui.preference.CarUiPreferenceViewStub.TWO_ACTION_TEXT_BORDERLESS;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.car.ui.plugin.oemapis.preference.PreferenceOEMV1;
import com.android.car.ui.plugin.oemapis.preference.PreferenceViewAttributesOEMV1;
import com.android.car.ui.preference.CarUiPreferenceViewStub.PreferenceType;
-import com.android.car.ui.sharedlibrary.R;
+import com.chassis.car.ui.plugin.R;
/**
* Adapter to load preference from plugin.
@@ -142,15 +145,24 @@
int sharedLibId = getSharedLibViewId(name);
int appViewId = getAppViewId(name);
View view = carUiPreferenceView.findViewById(sharedLibId);
- if (view != null) {
- view.setId(appViewId);
- }
+ ViewGroup.LayoutParams currentViewLayoutParam =
+ (ViewGroup.LayoutParams) view.getLayoutParams();
+ ViewGroup parent = (ViewGroup) view.getParent();
+ int index = parent.indexOfChild(view);
+ parent.removeView(view);
+ FrameLayout wrapper = new FrameLayout(mPluginContext);
+ ViewGroup.LayoutParams wrapperLayoutparams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ wrapper.addView(view, wrapperLayoutparams);
+ parent.addView(wrapper, index, currentViewLayoutParam);
+ view.setId(appViewId);
+ wrapper.setId(sharedLibId);
}
private int getSharedLibViewId(String resName) {
Resources res = mPluginContext.getResources();
return res.getIdentifier(resName, "id",
- "com.android.car.ui.sharedlibrary");
+ ((ContextWrapper) mPluginContext).getBaseContext().getPackageName());
}
private int getAppViewId(String resName) {
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/ListItemUtils.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/ListItemUtils.java
new file mode 100644
index 0000000..4bb3250
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/ListItemUtils.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.recyclerview;
+
+import android.text.SpannableString;
+
+import androidx.annotation.NonNull;
+
+import com.android.car.ui.CarUiText;
+import com.android.car.ui.plugin.oemapis.Consumer;
+import com.android.car.ui.plugin.oemapis.TextOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ContentListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ContentListItemOEMV2;
+import com.android.car.ui.plugin.oemapis.recyclerview.HeaderListItemOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.ListItemOEMV1;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiHeaderListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A utility class for converting between static and oem list items.
+ */
+public final class ListItemUtils {
+
+ /**
+ * The plugin was passed the list items as {@code ListItemOEMV1}s and thus must be converted
+ * back to use the "original" {@code CarUiListItem}s that's expected by the
+ * {@code CarUiListItemAdapter}
+ */
+ @NonNull
+ public static CarUiListItem toStaticListItem(@NonNull ListItemOEMV1 item) {
+ if (item instanceof HeaderListItemOEMV1) {
+ HeaderListItemOEMV1 header = (HeaderListItemOEMV1) item;
+ return new CarUiHeaderListItem(header.getTitle(), header.getBody());
+ } else if (item instanceof ContentListItemOEMV1) { // For backwards compatibility
+ ContentListItemOEMV1 contentItem = (ContentListItemOEMV1) item;
+
+ CarUiContentListItem listItem = new CarUiContentListItem(
+ toCarUiContentListItemActionV1(contentItem.getAction()));
+
+ if (contentItem.getTitle() != null) {
+ listItem.setTitle(toCarUiText(contentItem.getTitle()));
+ }
+
+ if (contentItem.getBody() != null) {
+ listItem.setBody(toCarUiText(contentItem.getBody()));
+ }
+
+ listItem.setIcon(contentItem.getIcon());
+ listItem.setPrimaryIconType(
+ toCarUiContentListItemIconTypeV1(contentItem.getPrimaryIconType()));
+
+ if (contentItem.getAction() == ContentListItemOEMV1.Action.ICON) {
+ CarUiContentListItem.OnClickListener listener =
+ contentItem.getSupplementalIconOnClickListener() != null
+ ? carUiContentListItem ->
+ contentItem.getSupplementalIconOnClickListener().accept(
+ contentItem) : null;
+
+
+ listItem.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
+ }
+
+ if (contentItem.getOnClickListener() != null) {
+ CarUiContentListItem.OnClickListener listener =
+ contentItem.getOnClickListener() != null
+ ? carUiContentListItem ->
+ contentItem.getOnClickListener().accept(
+ contentItem) : null;
+ listItem.setOnItemClickedListener(listener);
+ }
+
+ // Convert the {@code CarUiContentListItem} provided by the static list item's
+ // OnCheckedChangeListener callback to a {@code ListItemOEMV1} so that is is compatible
+ // with the provided {@code ListItemOEMV1}'s OnCheckedChangeListener which is of the
+ // form Consumer<ListItemOEMV1>
+ listItem.setOnCheckedChangeListener((carUiContentListItem, checked) -> {
+ carUiContentListItem.setChecked(checked);
+ if (contentItem.getOnCheckedChangeListener() != null) {
+ contentItem.getOnCheckedChangeListener().accept(
+ (ContentListItemOEMV1) toOemListItemV1(carUiContentListItem));
+ }
+ });
+
+ listItem.setActionDividerVisible(contentItem.isActionDividerVisible());
+ listItem.setEnabled(contentItem.isEnabled());
+ listItem.setChecked(contentItem.isChecked());
+ listItem.setActivated(contentItem.isActivated());
+ listItem.setSecure(contentItem.isSecure());
+ return listItem;
+ } else if (item instanceof ContentListItemOEMV2) {
+ ContentListItemOEMV2 contentItem = (ContentListItemOEMV2) item;
+
+ CarUiContentListItem listItem = new CarUiContentListItem(
+ toCarUiContentListItemActionV2(contentItem.getAction()));
+
+ if (contentItem.getTitle() != null) {
+ listItem.setTitle(toCarUiText(contentItem.getTitle()));
+ }
+
+ if (contentItem.getBody() != null) {
+ listItem.setBody(toCarUiText(contentItem.getBody()));
+ }
+
+ listItem.setIcon(contentItem.getIcon());
+ listItem.setPrimaryIconType(
+ toCarUiContentListItemIconTypeV2(contentItem.getPrimaryIconType()));
+
+ if (contentItem.getAction() == ContentListItemOEMV2.Action.ICON) {
+ CarUiContentListItem.OnClickListener listener =
+ contentItem.getSupplementalIconOnClickListener() != null
+ ? carUiContentListItem ->
+ contentItem.getSupplementalIconOnClickListener().accept(
+ contentItem) : null;
+
+
+ listItem.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
+ }
+
+ if (contentItem.getOnClickListener() != null) {
+ CarUiContentListItem.OnClickListener listener =
+ contentItem.getOnClickListener() != null
+ ? carUiContentListItem ->
+ contentItem.getOnClickListener().accept(
+ contentItem) : null;
+ listItem.setOnItemClickedListener(listener);
+ }
+
+ // Convert the {@code CarUiContentListItem} provided by the static list item's
+ // OnCheckedChangeListener callback to a {@code ListItemOEMV1} so that is is compatible
+ // with the provided {@code ListItemOEMV1}'s OnCheckedChangeListener which is of the
+ // form Consumer<ListItemOEMV1>
+ listItem.setOnCheckedChangeListener((carUiContentListItem, checked) -> {
+ carUiContentListItem.setChecked(checked);
+ if (contentItem.getOnCheckedChangeListener() != null) {
+ contentItem.getOnCheckedChangeListener().accept(
+ (ContentListItemOEMV2) toOemListItemV2(carUiContentListItem));
+ }
+ });
+
+ listItem.setActionDividerVisible(contentItem.isActionDividerVisible());
+ listItem.setEnabled(contentItem.isEnabled());
+ listItem.setChecked(contentItem.isChecked());
+ listItem.setActivated(contentItem.isActivated());
+ listItem.setSecure(contentItem.isSecure());
+ return listItem;
+ } else {
+ throw new IllegalStateException("Unknown view type.");
+ }
+ }
+
+ private static CarUiText toCarUiText(TextOEMV1 text) {
+ return new CarUiText.Builder(text.getTextVariants()).setMaxChars(
+ text.getMaxChars()).setMaxLines(text.getMaxLines()).build();
+ }
+
+ private static List<CarUiText> toCarUiText(List<TextOEMV1> lines) {
+ List<CarUiText> oemLines = new ArrayList<>();
+
+ for (TextOEMV1 line : lines) {
+ oemLines.add(new CarUiText.Builder(line.getTextVariants()).setMaxChars(
+ line.getMaxChars()).setMaxLines(line.getMaxLines()).build());
+ }
+ return oemLines;
+ }
+
+ // For backwards compatibility
+ private static CarUiContentListItem.Action toCarUiContentListItemActionV1(
+ ContentListItemOEMV1.Action action) {
+ switch (action) {
+ case NONE:
+ return CarUiContentListItem.Action.NONE;
+ case SWITCH:
+ return CarUiContentListItem.Action.SWITCH;
+ case CHECK_BOX:
+ return CarUiContentListItem.Action.CHECK_BOX;
+ case RADIO_BUTTON:
+ return CarUiContentListItem.Action.RADIO_BUTTON;
+ case ICON:
+ return CarUiContentListItem.Action.ICON;
+ case CHEVRON:
+ return CarUiContentListItem.Action.CHEVRON;
+ default:
+ throw new IllegalStateException("Unexpected list item action type");
+ }
+ }
+
+ private static CarUiContentListItem.Action toCarUiContentListItemActionV2(
+ ContentListItemOEMV2.Action action) {
+ switch (action) {
+ case NONE:
+ return CarUiContentListItem.Action.NONE;
+ case SWITCH:
+ return CarUiContentListItem.Action.SWITCH;
+ case CHECK_BOX:
+ return CarUiContentListItem.Action.CHECK_BOX;
+ case RADIO_BUTTON:
+ return CarUiContentListItem.Action.RADIO_BUTTON;
+ case ICON:
+ return CarUiContentListItem.Action.ICON;
+ case CHEVRON:
+ return CarUiContentListItem.Action.CHEVRON;
+ default:
+ throw new IllegalStateException("Unexpected list item action type");
+ }
+ }
+
+ // For backwards compatibility
+ private static CarUiContentListItem.IconType toCarUiContentListItemIconTypeV1(
+ ContentListItemOEMV1.IconType iconType) {
+ switch (iconType) {
+ case CONTENT:
+ return CarUiContentListItem.IconType.CONTENT;
+ case STANDARD:
+ return CarUiContentListItem.IconType.STANDARD;
+ case AVATAR:
+ return CarUiContentListItem.IconType.AVATAR;
+ default:
+ throw new IllegalStateException("Unexpected list item icon type");
+ }
+ }
+
+ private static CarUiContentListItem.IconType toCarUiContentListItemIconTypeV2(
+ ContentListItemOEMV2.IconType iconType) {
+ switch (iconType) {
+ case CONTENT:
+ return CarUiContentListItem.IconType.CONTENT;
+ case STANDARD:
+ return CarUiContentListItem.IconType.STANDARD;
+ case AVATAR:
+ return CarUiContentListItem.IconType.AVATAR;
+ default:
+ throw new IllegalStateException("Unexpected list item icon type");
+ }
+ }
+
+ // Below methods are necessary to convert from a static list item to an oem list item, which is
+ // needed to set a static list item's OnCheckedChangeListener
+ private static ListItemOEMV1 toOemListItemV2(CarUiListItem item) {
+ if (item instanceof CarUiHeaderListItem) {
+ CarUiHeaderListItem header = (CarUiHeaderListItem) item;
+ return new HeaderListItemOEMV1.Builder(new SpannableString(header.getTitle()))
+ .setBody(new SpannableString(header.getBody()))
+ .build();
+ } else if (item instanceof CarUiContentListItem) {
+ CarUiContentListItem contentItem = (CarUiContentListItem) item;
+
+ ContentListItemOEMV2.Builder builder = new ContentListItemOEMV2.Builder(
+ toOemListItemActionV2(contentItem.getAction()));
+
+ if (contentItem.getTitle() != null) {
+ builder.setTitle(toOemText(contentItem.getTitle()));
+ }
+
+ if (contentItem.getBody() != null) {
+ builder.setBody(toOemText(contentItem.getBody()));
+ }
+
+ builder.setIcon(contentItem.getIcon(),
+ toOemListItemIconTypeV2(contentItem.getPrimaryIconType()));
+
+ if (contentItem.getAction() == CarUiContentListItem.Action.ICON) {
+ Consumer<ContentListItemOEMV2> listener =
+ contentItem.getSupplementalIconOnClickListener() != null
+ ? oemItem ->
+ contentItem.getSupplementalIconOnClickListener().onClick(
+ contentItem) : null;
+ builder.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
+ }
+
+ if (contentItem.getOnClickListener() != null) {
+ Consumer<ContentListItemOEMV2> listener =
+ contentItem.getOnClickListener() != null
+ ? oemItem ->
+ contentItem.getOnClickListener().onClick(contentItem) : null;
+ builder.setOnItemClickedListener(listener);
+ }
+
+ builder.setOnCheckedChangeListener(oem -> contentItem.setChecked(oem.isChecked()))
+ .setActionDividerVisible(contentItem.isActionDividerVisible())
+ .setEnabled(contentItem.isEnabled())
+ .setChecked(contentItem.isChecked())
+ .setActivated(contentItem.isActivated())
+ .setSecure(contentItem.isSecure());
+ return builder.build();
+ } else {
+ throw new IllegalStateException("Unknown view type.");
+ }
+ }
+
+ // For backwards compatibility
+ private static ListItemOEMV1 toOemListItemV1(CarUiListItem item) {
+ if (item instanceof CarUiHeaderListItem) {
+ CarUiHeaderListItem header = (CarUiHeaderListItem) item;
+ return new HeaderListItemOEMV1.Builder(new SpannableString(header.getTitle()))
+ .setBody(new SpannableString(header.getBody()))
+ .build();
+ } else if (item instanceof CarUiContentListItem) {
+ CarUiContentListItem contentItem = (CarUiContentListItem) item;
+
+ ContentListItemOEMV1.Builder builder = new ContentListItemOEMV1.Builder(
+ toOemListItemActionV1(contentItem.getAction()));
+
+ if (contentItem.getTitle() != null) {
+ builder.setTitle(toOemText(contentItem.getTitle()));
+ }
+
+ if (contentItem.getBody() != null) {
+ builder.setBody(toOemText(contentItem.getBody()));
+ }
+
+ builder.setIcon(contentItem.getIcon(),
+ toOemListItemIconTypeV1(contentItem.getPrimaryIconType()));
+
+ if (contentItem.getAction() == CarUiContentListItem.Action.ICON) {
+ java.util.function.Consumer<ContentListItemOEMV1> listener =
+ contentItem.getSupplementalIconOnClickListener() != null
+ ? oemItem ->
+ contentItem.getSupplementalIconOnClickListener().onClick(
+ contentItem) : null;
+ builder.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
+ }
+
+ if (contentItem.getOnClickListener() != null) {
+ java.util.function.Consumer<ContentListItemOEMV1> listener =
+ contentItem.getOnClickListener() != null
+ ? oemItem ->
+ contentItem.getOnClickListener().onClick(contentItem) : null;
+ builder.setOnItemClickedListener(listener);
+ }
+
+ builder.setOnCheckedChangeListener(oem -> contentItem.setChecked(oem.isChecked()))
+ .setActionDividerVisible(contentItem.isActionDividerVisible())
+ .setEnabled(contentItem.isEnabled())
+ .setChecked(contentItem.isChecked())
+ .setActivated(contentItem.isActivated())
+ .setSecure(contentItem.isSecure());
+ return builder.build();
+ } else {
+ throw new IllegalStateException("Unknown view type.");
+ }
+ }
+
+ private static TextOEMV1 toOemText(CarUiText text) {
+ return new TextOEMV1.Builder(text.getTextVariants()).setMaxChars(
+ text.getMaxChars()).setMaxLines(text.getMaxLines()).build();
+ }
+
+ private static List<TextOEMV1> toOemText(List<CarUiText> lines) {
+ List<TextOEMV1> oemLines = new ArrayList<>();
+
+ for (CarUiText line : lines) {
+ oemLines.add(new TextOEMV1.Builder(line.getTextVariants()).setMaxChars(
+ line.getMaxChars()).setMaxLines(line.getMaxLines()).build());
+ }
+ return oemLines;
+ }
+
+ private static ContentListItemOEMV2.Action toOemListItemActionV2(
+ CarUiContentListItem.Action action) {
+ switch (action) {
+ case NONE:
+ return ContentListItemOEMV2.Action.NONE;
+ case SWITCH:
+ return ContentListItemOEMV2.Action.SWITCH;
+ case CHECK_BOX:
+ return ContentListItemOEMV2.Action.CHECK_BOX;
+ case RADIO_BUTTON:
+ return ContentListItemOEMV2.Action.RADIO_BUTTON;
+ case ICON:
+ return ContentListItemOEMV2.Action.ICON;
+ case CHEVRON:
+ return ContentListItemOEMV2.Action.CHEVRON;
+ default:
+ throw new IllegalStateException("Unexpected list item action type");
+ }
+ }
+
+ private static ContentListItemOEMV2.IconType toOemListItemIconTypeV2(
+ CarUiContentListItem.IconType iconType) {
+ switch (iconType) {
+ case CONTENT:
+ return ContentListItemOEMV2.IconType.CONTENT;
+ case STANDARD:
+ return ContentListItemOEMV2.IconType.STANDARD;
+ case AVATAR:
+ return ContentListItemOEMV2.IconType.AVATAR;
+ default:
+ throw new IllegalStateException("Unexpected list item icon type");
+ }
+ }
+
+ // For backwards compatibility
+ private static ContentListItemOEMV1.Action toOemListItemActionV1(
+ CarUiContentListItem.Action action) {
+ switch (action) {
+ case NONE:
+ return ContentListItemOEMV1.Action.NONE;
+ case SWITCH:
+ return ContentListItemOEMV1.Action.SWITCH;
+ case CHECK_BOX:
+ return ContentListItemOEMV1.Action.CHECK_BOX;
+ case RADIO_BUTTON:
+ return ContentListItemOEMV1.Action.RADIO_BUTTON;
+ case ICON:
+ return ContentListItemOEMV1.Action.ICON;
+ case CHEVRON:
+ return ContentListItemOEMV1.Action.CHEVRON;
+ default:
+ throw new IllegalStateException("Unexpected list item action type");
+ }
+ }
+
+ // For backwards compatibility
+ private static ContentListItemOEMV1.IconType toOemListItemIconTypeV1(
+ CarUiContentListItem.IconType iconType) {
+ switch (iconType) {
+ case CONTENT:
+ return ContentListItemOEMV1.IconType.CONTENT;
+ case STANDARD:
+ return ContentListItemOEMV1.IconType.STANDARD;
+ case AVATAR:
+ return ContentListItemOEMV1.IconType.AVATAR;
+ default:
+ throw new IllegalStateException("Unexpected list item icon type");
+ }
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV1.java
similarity index 89%
copy from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV1.java
index b81b235..b2a0efa 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV1.java
@@ -32,24 +32,25 @@
import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1;
import com.android.car.ui.plugin.oemapis.recyclerview.LayoutStyleOEMV1;
import com.android.car.ui.plugin.oemapis.recyclerview.OnChildAttachStateChangeListenerOEMV1;
+import com.android.car.ui.plugin.oemapis.recyclerview.OnScrollListenerOEMV1;
import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
-import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2;
+import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV1;
import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1;
import com.android.car.ui.recyclerview.CarUiRecyclerView;
import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl;
-import com.android.car.ui.recyclerview.RecyclerViewAdapterV1;
import java.util.ArrayList;
import java.util.List;
/**
- * Adapts a {@link RecyclerViewAdapterV1} into a {@link RecyclerViewOEMV2}.
+ * See {@code RecyclerViewAdapterProxyV2}. This class is for backwards compatibility with apps that
+ * use an older version of car-ui-lib.
*/
-public class RecyclerViewAdapterProxy implements RecyclerViewOEMV2 {
+public class RecyclerViewAdapterProxyV1 implements RecyclerViewOEMV1 {
private Context mPluginContext;
private CarUiRecyclerViewImpl mRecyclerView;
- public RecyclerViewAdapterProxy(Context pluginContext, CarUiRecyclerViewImpl recyclerView,
+ public RecyclerViewAdapterProxyV1(Context pluginContext, CarUiRecyclerViewImpl recyclerView,
RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
mPluginContext = pluginContext;
mRecyclerView = recyclerView;
@@ -57,23 +58,23 @@
}
@NonNull
- private final List<OnScrollListenerOEMV2> mScrollListeners = new ArrayList<>();
+ private final List<OnScrollListenerOEMV1> mScrollListeners = new ArrayList<>();
@NonNull
private final CarUiRecyclerView.OnScrollListener mOnScrollListener =
new CarUiRecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull CarUiRecyclerView recyclerView, int dx, int dy) {
- for (OnScrollListenerOEMV2 listener : mScrollListeners) {
- listener.onScrolled(RecyclerViewAdapterProxy.this, dx, dy);
+ for (OnScrollListenerOEMV1 listener : mScrollListeners) {
+ listener.onScrolled(RecyclerViewAdapterProxyV1.this, dx, dy);
}
}
@Override
public void onScrollStateChanged(@NonNull CarUiRecyclerView recyclerView,
int newState) {
- for (OnScrollListenerOEMV2 listener : mScrollListeners) {
- listener.onScrollStateChanged(RecyclerViewAdapterProxy.this,
+ for (OnScrollListenerOEMV1 listener : mScrollListeners) {
+ listener.onScrollStateChanged(RecyclerViewAdapterProxyV1.this,
toInternalScrollState(newState));
}
}
@@ -104,13 +105,13 @@
private static int toInternalScrollState(int state) {
/* default to RecyclerView.SCROLL_STATE_IDLE */
- int internalState = RecyclerViewOEMV2.SCROLL_STATE_IDLE;
+ int internalState = RecyclerViewOEMV1.SCROLL_STATE_IDLE;
switch (state) {
case RecyclerView.SCROLL_STATE_DRAGGING:
- internalState = RecyclerViewOEMV2.SCROLL_STATE_DRAGGING;
+ internalState = RecyclerViewOEMV1.SCROLL_STATE_DRAGGING;
break;
case RecyclerView.SCROLL_STATE_SETTLING:
- internalState = RecyclerViewOEMV2.SCROLL_STATE_SETTLING;
+ internalState = RecyclerViewOEMV1.SCROLL_STATE_SETTLING;
break;
}
return internalState;
@@ -127,16 +128,16 @@
}
@Override
- public void addOnScrollListener(@NonNull OnScrollListenerOEMV2 onScrollListenerOEMV2) {
+ public void addOnScrollListener(@NonNull OnScrollListenerOEMV1 onScrollListenerOEMV1) {
if (mScrollListeners.isEmpty()) {
mRecyclerView.addOnScrollListener(mOnScrollListener);
}
- mScrollListeners.add(onScrollListenerOEMV2);
+ mScrollListeners.add(onScrollListenerOEMV1);
}
@Override
- public void removeOnScrollListener(@NonNull OnScrollListenerOEMV2 onScrollListenerOEMV2) {
- mScrollListeners.remove(onScrollListenerOEMV2);
+ public void removeOnScrollListener(@NonNull OnScrollListenerOEMV1 onScrollListenerOEMV1) {
+ mScrollListeners.remove(onScrollListenerOEMV1);
if (mScrollListeners.isEmpty()) {
mRecyclerView.removeOnScrollListener(mOnScrollListener);
}
@@ -385,21 +386,6 @@
}
@Override
- public boolean isComputingLayout() {
- return !mRecyclerView.isLayoutCompleted();
- }
-
- @Override
- public void addOnLayoutCompleteListener(@Nullable Runnable runnable) {
- mRecyclerView.addOnLayoutCompleteListener(runnable);
- }
-
- @Override
- public void removeOnLayoutCompleteListener(@Nullable Runnable runnable) {
- mRecyclerView.removeOnLayoutCompleteListener(runnable);
- }
-
- @Override
public void scrollToPositionWithOffset(int i, int i1) {
mRecyclerView.scrollToPositionWithOffset(i, i1);
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV2.java
similarity index 98%
rename from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java
rename to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV2.java
index b81b235..732d77c 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/recyclerview/RecyclerViewAdapterProxyV2.java
@@ -45,11 +45,11 @@
/**
* Adapts a {@link RecyclerViewAdapterV1} into a {@link RecyclerViewOEMV2}.
*/
-public class RecyclerViewAdapterProxy implements RecyclerViewOEMV2 {
+public class RecyclerViewAdapterProxyV2 implements RecyclerViewOEMV2 {
private Context mPluginContext;
private CarUiRecyclerViewImpl mRecyclerView;
- public RecyclerViewAdapterProxy(Context pluginContext, CarUiRecyclerViewImpl recyclerView,
+ public RecyclerViewAdapterProxyV2(Context pluginContext, CarUiRecyclerViewImpl recyclerView,
RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) {
mPluginContext = pluginContext;
mRecyclerView = recyclerView;
@@ -65,7 +65,7 @@
@Override
public void onScrolled(@NonNull CarUiRecyclerView recyclerView, int dx, int dy) {
for (OnScrollListenerOEMV2 listener : mScrollListeners) {
- listener.onScrolled(RecyclerViewAdapterProxy.this, dx, dy);
+ listener.onScrolled(RecyclerViewAdapterProxyV2.this, dx, dy);
}
}
@@ -73,7 +73,7 @@
public void onScrollStateChanged(@NonNull CarUiRecyclerView recyclerView,
int newState) {
for (OnScrollListenerOEMV2 listener : mScrollListeners) {
- listener.onScrollStateChanged(RecyclerViewAdapterProxy.this,
+ listener.onScrollStateChanged(RecyclerViewAdapterProxyV2.this,
toInternalScrollState(newState));
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/BaseLayoutInstaller.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/BaseLayoutInstaller.java
new file mode 100644
index 0000000..ed315ab
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/BaseLayoutInstaller.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.toolbar;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.plugin.oemapis.Consumer;
+import com.android.car.ui.plugin.oemapis.FocusAreaOEMV1;
+import com.android.car.ui.plugin.oemapis.FocusParkingViewOEMV1;
+import com.android.car.ui.plugin.oemapis.Function;
+import com.android.car.ui.plugin.oemapis.InsetsOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV3;
+
+import com.chassis.car.ui.plugin.R;
+
+/**
+ * A helper class for implementing installBaseLayoutAround from {@code PluginFactory}
+ */
+public class BaseLayoutInstaller {
+
+ private static int sBaseLayoutId = 0;
+
+ /**
+ * Installs the base layout around the contentView. Optionally installs a toolbar and returns
+ * an implementation of {@code ToolbarControllerOEMV1} if the toolbar is enabled.
+ */
+ @Nullable
+ public static ToolbarControllerOEMV1 installBaseLayoutAroundV1(
+ @NonNull Context pluginContext,
+ @NonNull View contentView,
+ @Nullable java.util.function.Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen,
+ @Nullable java.util.function.Function<Context, FocusParkingViewOEMV1>
+ focusParkingViewFactory,
+ @Nullable java.util.function.Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ ToolbarControllerImpl toolbarControllerImpl = installBaseLayoutAround(
+ pluginContext, contentView, insetsChangedListener != null
+ ? (Consumer<InsetsOEMV1>) insets -> insetsChangedListener.accept(insets)
+ : null, toolbarEnabled, fullscreen,
+ focusParkingViewFactory != null ? ((Function<Context, FocusParkingViewOEMV1>)
+ context -> focusParkingViewFactory.apply(context)) : null,
+ focusAreaFactory != null ? ((Function<Context, FocusAreaOEMV1>)
+ context -> focusAreaFactory.apply(context)) : null);
+ return !toolbarEnabled ? null : new ToolbarAdapterProxyV1(pluginContext,
+ toolbarControllerImpl);
+ }
+
+ /**
+ * Installs the base layout around the contentView. Optionally installs a toolbar and returns
+ * an implementation of {@code ToolbarControllerOEMV2} if the toolbar is enabled.
+ */
+ @Nullable
+ public static ToolbarControllerOEMV2 installBaseLayoutAroundV2(
+ @NonNull Context pluginContext,
+ @NonNull View contentView,
+ @Nullable Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen,
+ @Nullable Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ @Nullable Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ ToolbarControllerImpl toolbarControllerImpl = installBaseLayoutAround(
+ pluginContext, contentView, insetsChangedListener, toolbarEnabled, fullscreen,
+ focusParkingViewFactory, focusAreaFactory);
+ return !toolbarEnabled ? null : new ToolbarAdapterProxyV2(pluginContext,
+ toolbarControllerImpl);
+ }
+
+ /**
+ * Installs the base layout around the contentView. Optionally installs a toolbar and returns
+ * an implementation of {@code ToolbarControllerOEMV3} if the toolbar is enabled.
+ */
+ @Nullable
+ public static ToolbarControllerOEMV3 installBaseLayoutAroundV3(
+ @NonNull Context pluginContext,
+ @NonNull View contentView,
+ @Nullable Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullscreen,
+ @Nullable Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ @Nullable Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+ ToolbarControllerImpl toolbarControllerImpl = installBaseLayoutAround(
+ pluginContext, contentView, insetsChangedListener, toolbarEnabled, fullscreen,
+ focusParkingViewFactory, focusAreaFactory);
+ return !toolbarEnabled ? null : new ToolbarAdapterProxyV3(pluginContext,
+ toolbarControllerImpl);
+ }
+
+ /**
+ * Implementation of installBaseLayoutAround from {@code PluginFactory}
+ */
+ private static ToolbarControllerImpl installBaseLayoutAround(
+ @NonNull Context pluginContext,
+ @NonNull View contentView,
+ @Nullable Consumer<InsetsOEMV1> insetsChangedListener,
+ boolean toolbarEnabled,
+ boolean fullScreen,
+ @Nullable Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
+ @Nullable Function<Context, FocusAreaOEMV1> focusAreaFactory) {
+
+ @LayoutRes int layout = toolbarEnabled
+ ? R.layout.car_ui_portrait_base_layout_toolbar
+ : R.layout.car_ui_base_layout;
+
+ sBaseLayoutId = R.id.car_ui_base_layout_content_container;
+ ViewGroup baseLayout = (ViewGroup) LayoutInflater.from(pluginContext).inflate(
+ layout, null, false);
+
+ // Replace the app's content view with a base layout
+ ViewGroup contentViewParent = (ViewGroup) contentView.getParent();
+ int contentIndex = contentViewParent.indexOfChild(contentView);
+ contentViewParent.removeView(contentView);
+ contentViewParent.addView(baseLayout, contentIndex, contentView.getLayoutParams());
+
+ // Add the app's content view to the baseLayout's content view container
+ FrameLayout contentViewContainer = baseLayout.requireViewById(
+ R.id.car_ui_base_layout_content_container);
+ contentViewContainer.addView(contentView, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ ToolbarControllerImpl toolbarController = null;
+ if (toolbarEnabled) {
+ toolbarController = new ToolbarControllerImpl(baseLayout, pluginContext);
+ }
+
+ InsetsUpdater updater = new InsetsUpdater(baseLayout, contentView);
+ updater.replaceInsetsChangedListenerWith(insetsChangedListener);
+ updater.installListeners();
+
+ return toolbarController;
+ }
+
+ /**
+ * InsetsUpdater waits for layout changes, and when there is one, calculates the appropriate
+ * insets into the content view.
+ */
+ private static final class InsetsUpdater implements ViewTreeObserver.OnGlobalLayoutListener {
+ // These tags mark views that should overlay the content view in the base layout.
+ // Apps will then be able to draw under these views, but will be encouraged to not put
+ // any user-interactable content there.
+ private static final String LEFT_INSET_TAG = "car_ui_portrait_left_inset";
+ private static final String RIGHT_INSET_TAG = "car_ui_portrait_right_inset";
+ private static final String TOP_INSET_TAG = "car_ui_portrait_top_inset";
+ private static final String BOTTOM_INSET_TAG = "car_ui_portrait_bottom_inset";
+
+ private final View mContentView;
+ private final View mContentViewContainer; // Equivalent to mContentView except in Media
+ private final View mLeftInsetView;
+ private final View mRightInsetView;
+ private final View mTopInsetView;
+ private final View mBottomInsetView;
+ private Consumer<InsetsOEMV1> mInsetsChangedListenerDelegate;
+
+ private boolean mInsetsDirty = true;
+ private InsetsOEMV1 mInsets = new InsetsOEMV1();
+
+ /**
+ * Constructs an InsetsUpdater that calculates and dispatches insets to the method provided
+ * via {@link #replaceInsetsChangedListenerWith(Consumer)}.
+ *
+ * @param baseLayout The root view of the base layout
+ * @param contentView The android.R.id.content View
+ */
+ InsetsUpdater(
+ View baseLayout,
+ View contentView) {
+ mContentView = contentView;
+ mContentViewContainer = baseLayout.requireViewById(sBaseLayoutId);
+
+ mLeftInsetView = baseLayout.findViewWithTag(LEFT_INSET_TAG);
+ mRightInsetView = baseLayout.findViewWithTag(RIGHT_INSET_TAG);
+ mTopInsetView = baseLayout.findViewWithTag(TOP_INSET_TAG);
+ mBottomInsetView = baseLayout.findViewWithTag(BOTTOM_INSET_TAG);
+
+ final View.OnLayoutChangeListener layoutChangeListener =
+ (View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) -> {
+ if (left != oldLeft || top != oldTop
+ || right != oldRight || bottom != oldBottom) {
+ mInsetsDirty = true;
+ }
+ };
+
+ if (mLeftInsetView != null) {
+ mLeftInsetView.addOnLayoutChangeListener(layoutChangeListener);
+ }
+ if (mRightInsetView != null) {
+ mRightInsetView.addOnLayoutChangeListener(layoutChangeListener);
+ }
+ if (mTopInsetView != null) {
+ mTopInsetView.addOnLayoutChangeListener(layoutChangeListener);
+ }
+ if (mBottomInsetView != null) {
+ mBottomInsetView.addOnLayoutChangeListener(layoutChangeListener);
+ }
+ contentView.addOnLayoutChangeListener(layoutChangeListener);
+ mContentViewContainer.addOnLayoutChangeListener(layoutChangeListener);
+ }
+
+ /**
+ * Install a global layout listener, during which the insets will be recalculated and
+ * dispatched.
+ */
+ public void installListeners() {
+ // The global layout listener will run after all the individual layout change listeners
+ // so that we only updateInsets once per layout, even if multiple inset views changed
+ mContentView.getRootView().getViewTreeObserver()
+ .addOnGlobalLayoutListener(this);
+ }
+
+ InsetsOEMV1 getInsets() {
+ return mInsets;
+ }
+
+ // TODO remove this method / cleanup this class
+ public void replaceInsetsChangedListenerWith(Consumer<InsetsOEMV1> listener) {
+ mInsetsChangedListenerDelegate = listener;
+ }
+
+ /**
+ * onGlobalLayout() should recalculate the amount of insets we need, and then dispatch them.
+ */
+ @Override
+ public void onGlobalLayout() {
+ if (!mInsetsDirty) {
+ return;
+ }
+
+ // Calculate how much each inset view overlays the content view
+
+ // These initial values are for Media Center's implementation of base layouts.
+ // They should evaluate to 0 in all other apps, because the content view and content
+ // view container have the same size and position there.
+ int top = Math.max(0,
+ getTopOfView(mContentViewContainer) - getTopOfView(mContentView));
+ int left = Math.max(0,
+ getLeftOfView(mContentViewContainer) - getLeftOfView(mContentView));
+ int right = Math.max(0,
+ getRightOfView(mContentView) - getRightOfView(mContentViewContainer));
+ int bottom = Math.max(0,
+ getBottomOfView(mContentView) - getBottomOfView(mContentViewContainer));
+ if (mTopInsetView != null) {
+ top += Math.max(0,
+ getBottomOfView(mTopInsetView) - getTopOfView(mContentViewContainer));
+ }
+ if (mBottomInsetView != null) {
+ bottom += Math.max(0,
+ getBottomOfView(mContentViewContainer) - getTopOfView(mBottomInsetView));
+ }
+ if (mLeftInsetView != null) {
+ left += Math.max(0,
+ getRightOfView(mLeftInsetView) - getLeftOfView(mContentViewContainer));
+ }
+ if (mRightInsetView != null) {
+ right += Math.max(0,
+ getRightOfView(mContentViewContainer) - getLeftOfView(mRightInsetView));
+ }
+ InsetsOEMV1 insets = new InsetsOEMV1(left, top, right, bottom);
+
+ mInsetsDirty = false;
+ if (!insets.equals(mInsets)) {
+ mInsets = insets;
+ if (mInsetsChangedListenerDelegate != null) {
+ mInsetsChangedListenerDelegate.accept(insets);
+ }
+ }
+ }
+
+ private static int getLeftOfView(View v) {
+ int[] position = new int[2];
+ v.getLocationOnScreen(position);
+ return position[0];
+ }
+
+ private static int getRightOfView(View v) {
+ int[] position = new int[2];
+ v.getLocationOnScreen(position);
+ return position[0] + v.getWidth();
+ }
+
+ private static int getTopOfView(View v) {
+ int[] position = new int[2];
+ v.getLocationOnScreen(position);
+ return position[1];
+ }
+
+ private static int getBottomOfView(View v) {
+ int[] position = new int[2];
+ v.getLocationOnScreen(position);
+ return position[1] + v.getHeight();
+ }
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ImeSearchInterfaceProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ImeSearchInterfaceProxy.java
new file mode 100644
index 0000000..5c515a6
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ImeSearchInterfaceProxy.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.toolbar;
+
+import android.os.Bundle;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.plugin.oemapis.toolbar.ImeSearchInterfaceOEMV1;
+import com.android.car.ui.plugin.oemapis.toolbar.ImeSearchInterfaceOEMV2;
+
+/**
+ * Adapts an ImeSearchInterface for backwards compatibility with apps that use an older version of
+ * car-ui-lib.
+ */
+public final class ImeSearchInterfaceProxy {
+ /**
+ * Converts an ImeSearchInterfaceOEMV2 from {@code ToolbarController#getImeSearchInterface} to
+ * an ImeSearchInterfaceOEMV1 which must be returned from an {@code ToolbarControllerOEMV1}.
+ */
+ @NonNull
+ public static ImeSearchInterfaceOEMV1 getImeSearchInterfaceV1(
+ @NonNull ToolbarControllerImpl toolbarController) {
+ ImeSearchInterfaceOEMV2 imeSearchInterface = toolbarController.getImeSearchInterface();
+ return new ImeSearchInterfaceOEMV1() {
+ @Override
+ public void setSearchTextViewConsumer(
+ @Nullable java.util.function.Consumer<TextView> consumer) {
+ imeSearchInterface.setSearchTextViewConsumer(
+ (TextView tv) -> consumer.accept(tv));
+ }
+
+ @Override
+ public void setOnPrivateImeCommandListener(
+ @Nullable java.util.function.BiConsumer<String, Bundle> biConsumer) {
+ imeSearchInterface.setOnPrivateImeCommandListener(
+ (String s, Bundle b) -> biConsumer.accept(s, b));
+ }
+ };
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/MenuItemRenderer.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/MenuItemRenderer.java
new file mode 100644
index 0000000..e269be9
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/MenuItemRenderer.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.toolbar;
+
+import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId;
+
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.LayoutRes;
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
+
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.uxr.DrawableStateView;
+
+import com.chassis.car.ui.plugin.R;
+
+import java.util.function.Consumer;
+
+class MenuItemRenderer implements MenuItem.Listener {
+
+ private static final int[] RESTRICTED_STATE = new int[]{R.attr.state_ux_restricted};
+
+ private final int mMenuItemIconSize;
+
+ private boolean mToolbarIsSearching;
+
+ private final MenuItem mMenuItem;
+ private final ViewGroup mParentView;
+ private View mView;
+ private View mIconContainer;
+ private ImageView mIconView;
+ private Switch mSwitch;
+ private TextView mTextView;
+ private TextView mTextWithIconView;
+ private boolean mIndividualClickListeners;
+
+ MenuItemRenderer(MenuItem item, ViewGroup parentView) {
+ mMenuItem = item;
+ mParentView = parentView;
+ mMenuItem.setListener(this);
+
+ mMenuItemIconSize = parentView.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.car_ui_toolbar_menu_item_icon_size);
+ }
+
+ void setToolbarIsSearching(boolean searching) {
+ if (searching != mToolbarIsSearching) {
+ mToolbarIsSearching = searching;
+
+ if (mMenuItem.isSearch()) {
+ updateView();
+ }
+ }
+ }
+
+ @Override
+ public void onMenuItemChanged(MenuItem changedItem) {
+ updateView();
+ }
+
+ void createView(Consumer<View> callback) {
+ AsyncLayoutInflater inflater = new AsyncLayoutInflater(mParentView.getContext());
+ @LayoutRes int layout = mMenuItem.isPrimary()
+ ? R.layout.car_ui_toolbar_menu_item_primary
+ : R.layout.car_ui_toolbar_menu_item;
+ inflater.inflate(layout, mParentView, (View view, int resid,
+ ViewGroup parent) -> {
+ mView = view;
+
+ mIconContainer =
+ requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_icon_container);
+ mIconView = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_icon);
+ mSwitch = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_switch);
+ mTextView = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_text);
+ mTextWithIconView =
+ requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_text_with_icon);
+ mIndividualClickListeners = mView.getContext().getResources()
+ .getBoolean(R.bool.car_ui_toolbar_menuitem_individual_click_listeners);
+
+ updateView();
+ callback.accept(mView);
+ });
+ }
+
+ private void updateView() {
+ if (mView == null) {
+ return;
+ }
+
+ mView.setId(mMenuItem.getId());
+
+ boolean hasIcon = mMenuItem.getIcon() != null;
+ boolean hasText = !TextUtils.isEmpty(mMenuItem.getTitle());
+ boolean textAndIcon = mMenuItem.isShowingIconAndTitle();
+ boolean checkable = mMenuItem.isCheckable();
+
+ if (!mMenuItem.isVisible()
+ || (mMenuItem.isSearch() && mToolbarIsSearching)
+ || (!checkable && !hasIcon && !hasText)) {
+ mView.setVisibility(View.GONE);
+ return;
+ }
+ mView.setVisibility(View.VISIBLE);
+ mView.setContentDescription(mMenuItem.getTitle());
+
+ View clickTarget;
+ if (checkable) {
+ mSwitch.setChecked(mMenuItem.isChecked());
+ clickTarget = mSwitch;
+ } else if (hasText && hasIcon && textAndIcon) {
+ mMenuItem.getIcon().setBounds(0, 0, mMenuItemIconSize, mMenuItemIconSize);
+ mTextWithIconView.setCompoundDrawables(mMenuItem.getIcon(), null, null, null);
+ mTextWithIconView.setText(mMenuItem.getTitle());
+ clickTarget = mTextWithIconView;
+ } else if (hasIcon) {
+ mIconView.setImageDrawable(mMenuItem.getIcon());
+ clickTarget = mIconContainer;
+ } else { // hasText will be true
+ mTextView.setText(mMenuItem.getTitle());
+ clickTarget = mTextView;
+ }
+
+ mIconContainer.setVisibility(clickTarget == mIconContainer ? View.VISIBLE : View.GONE);
+ mTextView.setVisibility(clickTarget == mTextView ? View.VISIBLE : View.GONE);
+ mTextWithIconView.setVisibility(clickTarget == mTextWithIconView
+ ? View.VISIBLE : View.GONE);
+ mSwitch.setVisibility(clickTarget == mSwitch ? View.VISIBLE : View.GONE);
+
+ if (!mIndividualClickListeners) {
+ clickTarget = mView;
+ }
+
+ if (!mMenuItem.isTinted() && hasIcon) {
+ mMenuItem.getIcon().setTintList(null);
+ }
+
+ recursiveSetEnabledAndDrawableState(mView);
+ mView.setActivated(mMenuItem.isActivated());
+
+ if (mMenuItem.getOnClickListener() != null
+ || mMenuItem.isCheckable()
+ || mMenuItem.isActivatable()) {
+ clickTarget.setOnClickListener(v -> mMenuItem.performClick());
+ } else if (clickTarget == mView) {
+ mView.setOnClickListener(null);
+ mView.setClickable(false);
+ }
+ }
+
+ private void recursiveSetEnabledAndDrawableState(View view) {
+ view.setEnabled(mMenuItem.isEnabled());
+
+ int[] drawableState = mMenuItem.isRestricted() ? RESTRICTED_STATE : null;
+ if (view instanceof ImageView) {
+ ((ImageView) view).setImageState(drawableState, true);
+ } else if (view instanceof DrawableStateView) {
+ ((DrawableStateView) view).setExtraDrawableState(drawableState, null);
+ }
+
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = ((ViewGroup) view);
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ recursiveSetEnabledAndDrawableState(viewGroup.getChildAt(i));
+ }
+ }
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ProgressBarControllerImpl.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ProgressBarControllerImpl.java
new file mode 100644
index 0000000..af76a82
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ProgressBarControllerImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.toolbar;
+
+import android.view.View;
+import android.widget.ProgressBar;
+
+import androidx.annotation.NonNull;
+
+import com.android.car.ui.toolbar.ProgressBarController;
+
+/**
+ * Implementation of {@link ProgressBarController}.
+ *
+ * <p>This class accepts a {@link ProgressBar} in its constructor and forwards the methods
+ * of {@link ProgressBarController} to it.
+ */
+class ProgressBarControllerImpl implements ProgressBarController {
+
+ @NonNull
+ private final ProgressBar mProgressBar;
+
+ ProgressBarControllerImpl(@NonNull ProgressBar progressBar) {
+ mProgressBar = progressBar;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ mProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mProgressBar.getVisibility() == View.VISIBLE;
+ }
+
+ @Override
+ public void setIndeterminate(boolean indeterminate) {
+ mProgressBar.setIndeterminate(indeterminate);
+ }
+
+ @Override
+ public boolean isIndeterminate() {
+ return mProgressBar.isIndeterminate();
+ }
+
+ @Override
+ public void setMax(int max) {
+ mProgressBar.setMax(max);
+ }
+
+ @Override
+ public int getMax() {
+ return mProgressBar.getMax();
+ }
+
+ @Override
+ public void setMin(int min) {
+ mProgressBar.setMin(min);
+ }
+
+ @Override
+ public int getMin() {
+ return mProgressBar.getMin();
+ }
+
+ @Override
+ public void setProgress(int progress) {
+ mProgressBar.setProgress(progress);
+ }
+
+ @Override
+ public int getProgress() {
+ return mProgressBar.getProgress();
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/TabLayout.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/TabLayout.java
new file mode 100644
index 0000000..2be6fc1
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/TabLayout.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.chassis.car.ui.plugin.toolbar;
+
+import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.chassis.car.ui.plugin.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Custom tab layout which supports adding tabs dynamically
+ *
+ * <p>It supports two layout modes:
+ * <ul><li>Flexible layout which will fill the width
+ * <li>Non-flexible layout which wraps content with a minimum tab width. By setting tab gravity,
+ * it can left aligned, right aligned or center aligned.
+ *
+ * <p>Scrolling function is not supported. If a tab item runs out of the tab layout bound, there
+ * is no way to access it. It's better to set the layout mode to flexible in this case.
+ *
+ * <p>Default tab item inflates from R.layout.car_ui_tab_item, but it also supports custom layout
+ * id, by overlaying R.layout.car_ui_tab_item_layout. By doing this, appearance of tab item view
+ * can be customized.
+ *
+ * <p>Touch feedback is using @android:attr/selectableItemBackground.
+ */
+public class TabLayout extends LinearLayout {
+ @LayoutRes
+ private final int mTabLayoutRes;
+ @NonNull
+ private List<com.android.car.ui.toolbar.Tab> mTabs = Collections.emptyList();
+ private int mSelectedTab = -1;
+
+ public TabLayout(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ Resources resources = context.getResources();
+
+ mTabLayoutRes = R.layout.car_ui_portrait_toolbar_tab_item;
+ }
+
+ /** Sets the tabs to show */
+ public void setTabs(List<com.android.car.ui.toolbar.Tab> tabs, int selectedTab) {
+ if (tabs == null) {
+ mTabs = Collections.emptyList();
+ } else {
+ mTabs = Collections.unmodifiableList(new ArrayList<>(tabs));
+ }
+ mSelectedTab = selectedTab;
+ recreateViews();
+ }
+
+ public List<com.android.car.ui.toolbar.Tab> getTabs() {
+ return mTabs;
+ }
+
+ /** Returns the currently selected tab, or -1 if no tabs exist */
+ public int getSelectedTab() {
+ if (mTabs.isEmpty() && mSelectedTab != -1) {
+ throw new IllegalStateException("Selected tab is invalid: " + mSelectedTab);
+ }
+ return mSelectedTab;
+ }
+
+ /**
+ * Returns if this TabLayout has tabs. That is, if the most recent call to
+ * {@link #setTabs(List, int)} contained a non-empty list.
+ */
+ public boolean hasTabs() {
+ return !mTabs.isEmpty();
+ }
+
+ /** Set the tab at given position as the current selected tab. */
+ public void selectTab(int position) {
+ if (position < 0 || position >= mTabs.size()) {
+ throw new IllegalArgumentException("Tab position is invalid: " + position);
+ }
+ if (position == mSelectedTab) {
+ return;
+ }
+
+ int oldPosition = mSelectedTab;
+ mSelectedTab = position;
+ presentTabView(oldPosition);
+ presentTabView(position);
+
+ com.android.car.ui.toolbar.Tab tab = mTabs.get(position);
+ Consumer<com.android.car.ui.toolbar.Tab> listener = tab.getSelectedListener();
+ if (listener != null) {
+ listener.accept(tab);
+ }
+ }
+
+ private void recreateViews() {
+ removeAllViews();
+ for (int i = 0; i < mTabs.size(); i++) {
+ View tabView = LayoutInflater.from(getContext())
+ .inflate(mTabLayoutRes, this, false);
+ addView(tabView);
+ presentTabView(i);
+ }
+ }
+
+ private void presentTabView(int position) {
+ if (position < 0 || position >= mTabs.size()) {
+ throw new IllegalArgumentException("Tab position is invalid: " + position);
+ }
+ View tabView = getChildAt(position);
+ com.android.car.ui.toolbar.Tab tab = mTabs.get(position);
+ ImageView iconView = requireViewByRefId(tabView,
+ R.id.car_ui_portrait_toolbar_tab_item_icon);
+ TextView textView = requireViewByRefId(tabView, R.id.car_ui_portrait_toolbar_tab_item_text);
+
+ tabView.setOnClickListener(view -> selectTab(position));
+ tabView.setActivated(position == mSelectedTab);
+
+ iconView.setImageDrawable(tab.getIcon());
+
+ textView.setText(tab.getText());
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV1.java
similarity index 84%
copy from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV1.java
index e86f646..6473500 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV1.java
@@ -22,34 +22,39 @@
import androidx.annotation.NonNull;
-import com.android.car.ui.plugin.oemapis.Consumer;
-import com.android.car.ui.plugin.oemapis.toolbar.ImeSearchInterfaceOEMV2;
+import com.android.car.ui.plugin.oemapis.toolbar.ImeSearchInterfaceOEMV1;
import com.android.car.ui.plugin.oemapis.toolbar.MenuItemOEMV1;
import com.android.car.ui.plugin.oemapis.toolbar.ProgressBarControllerOEMV1;
import com.android.car.ui.plugin.oemapis.toolbar.TabOEMV1;
-import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV1;
import com.android.car.ui.toolbar.MenuItem;
import com.android.car.ui.toolbar.NavButtonMode;
import com.android.car.ui.toolbar.SearchMode;
import com.android.car.ui.toolbar.Tab;
-import com.android.car.ui.toolbar.ToolbarControllerImpl;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
- * Wrapper class that passes the data to car-ui via ToolbarControllerOEMV1 interface
+ * See {@code ToolbarAdapterProxyV3}. This class is for backwards compatibility with apps that use
+ * an older version of car-ui-lib.
*/
-public final class ToolbarAdapterProxy implements ToolbarControllerOEMV2 {
+public final class ToolbarAdapterProxyV1 implements ToolbarControllerOEMV1 {
private final Context mPluginContext;
private final ToolbarControllerImpl mToolbarController;
- public ToolbarAdapterProxy(
+ public ToolbarAdapterProxyV1(
@NonNull Context pluginContext, @NonNull ToolbarControllerImpl toolbarController) {
mPluginContext = pluginContext;
mToolbarController = toolbarController;
+ // Set to true so that this toolbar will show menu items when in search mode if appropriate.
+ // For example, the plugin adapter will call setMenuItems with null when its corresponding
+ // toolbar's showMenuItemsWhileSearching is false, and call setMenuItems with menu items
+ // (not null) when showMenuItemsWhileSearching is true.
+ mToolbarController.setShowMenuItemsWhileSearching(true);
}
@Override
@@ -128,8 +133,8 @@
}
@Override
- public ImeSearchInterfaceOEMV2 getImeSearchInterface() {
- return mToolbarController.getImeSearchInterface();
+ public ImeSearchInterfaceOEMV1 getImeSearchInterface() {
+ return ImeSearchInterfaceProxy.getImeSearchInterfaceV1(mToolbarController);
}
@Override
@@ -169,15 +174,13 @@
if (menuItemOEMV1.isCheckable()) menuItemBuilder.setCheckable();
menuItemBuilder
- .setActivated(menuItemOEMV1.isActivated())
- .setChecked(menuItemOEMV1.isChecked())
.setEnabled(menuItemOEMV1.isEnabled())
.setVisible(menuItemOEMV1.isVisible())
.setPrimary(menuItemOEMV1.isPrimary())
.setTinted(menuItemOEMV1.isTinted())
.setTitle(menuItemOEMV1.getTitle())
.setIcon(menuItemOEMV1.getIcon())
- .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 1
+ .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 0
? MenuItem.DisplayBehavior.ALWAYS : MenuItem.DisplayBehavior.NEVER)
.setShowIconAndTitle(menuItemOEMV1.isShowingIconAndTitle())
.setOnClickListener(menuItem -> {
@@ -187,12 +190,26 @@
})
.setId(menuItemOEMV1.getKey());
+ if (menuItemOEMV1.isActivatable()) {
+ menuItemBuilder.setActivatable();
+ }
+ if (menuItemOEMV1.isCheckable()) {
+ menuItemBuilder.setCheckable();
+ }
+ if (menuItemOEMV1.isActivated()) {
+ menuItemBuilder.setActivated(menuItemOEMV1.isActivated());
+ }
+ if (menuItemOEMV1.isChecked()) {
+ menuItemBuilder.setChecked(menuItemOEMV1.isChecked());
+ }
+
menuItemBuilder.setUxRestrictions(menuItemOEMV1.isRestricted()
? CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED
: CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
return menuItemBuilder.build();
}
+
@Override
public void setSearchListener(Consumer<String> consumer) {
if (consumer != null) {
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV2.java
similarity index 88%
rename from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
rename to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV2.java
index e86f646..2cc6894 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV2.java
@@ -32,24 +32,28 @@
import com.android.car.ui.toolbar.NavButtonMode;
import com.android.car.ui.toolbar.SearchMode;
import com.android.car.ui.toolbar.Tab;
-import com.android.car.ui.toolbar.ToolbarControllerImpl;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
- * Wrapper class that passes the data to car-ui via ToolbarControllerOEMV1 interface
+ * Wrapper class that passes the data to car-ui via ToolbarControllerOEMV2 interface
*/
-public final class ToolbarAdapterProxy implements ToolbarControllerOEMV2 {
+public final class ToolbarAdapterProxyV2 implements ToolbarControllerOEMV2 {
private final Context mPluginContext;
private final ToolbarControllerImpl mToolbarController;
- public ToolbarAdapterProxy(
+ public ToolbarAdapterProxyV2(
@NonNull Context pluginContext, @NonNull ToolbarControllerImpl toolbarController) {
mPluginContext = pluginContext;
mToolbarController = toolbarController;
+ // Set to true so that this toolbar will show menu items when in search mode if appropriate.
+ // For example, the plugin adapter will call setMenuItems with null when its corresponding
+ // toolbar's showMenuItemsWhileSearching is false, and call setMenuItems with menu items
+ // (not null) when showMenuItemsWhileSearching is true.
+ mToolbarController.setShowMenuItemsWhileSearching(true);
}
@Override
@@ -169,15 +173,13 @@
if (menuItemOEMV1.isCheckable()) menuItemBuilder.setCheckable();
menuItemBuilder
- .setActivated(menuItemOEMV1.isActivated())
- .setChecked(menuItemOEMV1.isChecked())
.setEnabled(menuItemOEMV1.isEnabled())
.setVisible(menuItemOEMV1.isVisible())
.setPrimary(menuItemOEMV1.isPrimary())
.setTinted(menuItemOEMV1.isTinted())
.setTitle(menuItemOEMV1.getTitle())
.setIcon(menuItemOEMV1.getIcon())
- .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 1
+ .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 0
? MenuItem.DisplayBehavior.ALWAYS : MenuItem.DisplayBehavior.NEVER)
.setShowIconAndTitle(menuItemOEMV1.isShowingIconAndTitle())
.setOnClickListener(menuItem -> {
@@ -187,12 +189,26 @@
})
.setId(menuItemOEMV1.getKey());
+ if (menuItemOEMV1.isActivatable()) {
+ menuItemBuilder.setActivatable();
+ }
+ if (menuItemOEMV1.isCheckable()) {
+ menuItemBuilder.setCheckable();
+ }
+ if (menuItemOEMV1.isActivated()) {
+ menuItemBuilder.setActivated(menuItemOEMV1.isActivated());
+ }
+ if (menuItemOEMV1.isChecked()) {
+ menuItemBuilder.setChecked(menuItemOEMV1.isChecked());
+ }
+
menuItemBuilder.setUxRestrictions(menuItemOEMV1.isRestricted()
? CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED
: CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
return menuItemBuilder.build();
}
+
@Override
public void setSearchListener(Consumer<String> consumer) {
if (consumer != null) {
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV3.java
similarity index 86%
copy from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
copy to car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV3.java
index e86f646..3a48521 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxy.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarAdapterProxyV3.java
@@ -27,29 +27,33 @@
import com.android.car.ui.plugin.oemapis.toolbar.MenuItemOEMV1;
import com.android.car.ui.plugin.oemapis.toolbar.ProgressBarControllerOEMV1;
import com.android.car.ui.plugin.oemapis.toolbar.TabOEMV1;
-import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV2;
+import com.android.car.ui.plugin.oemapis.toolbar.ToolbarControllerOEMV3;
import com.android.car.ui.toolbar.MenuItem;
import com.android.car.ui.toolbar.NavButtonMode;
import com.android.car.ui.toolbar.SearchMode;
import com.android.car.ui.toolbar.Tab;
-import com.android.car.ui.toolbar.ToolbarControllerImpl;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
- * Wrapper class that passes the data to car-ui via ToolbarControllerOEMV1 interface
+ * Wrapper class that passes the data to car-ui via ToolbarControllerOEMV3 interface
*/
-public final class ToolbarAdapterProxy implements ToolbarControllerOEMV2 {
+public final class ToolbarAdapterProxyV3 implements ToolbarControllerOEMV3 {
private final Context mPluginContext;
private final ToolbarControllerImpl mToolbarController;
- public ToolbarAdapterProxy(
+ public ToolbarAdapterProxyV3(
@NonNull Context pluginContext, @NonNull ToolbarControllerImpl toolbarController) {
mPluginContext = pluginContext;
mToolbarController = toolbarController;
+ // Set to true so that this toolbar will show menu items when in search mode if appropriate.
+ // For example, the plugin adapter will call setMenuItems with null when its corresponding
+ // toolbar's showMenuItemsWhileSearching is false, and call setMenuItems with menu items
+ // (not null) when showMenuItemsWhileSearching is true.
+ mToolbarController.setShowMenuItemsWhileSearching(true);
}
@Override
@@ -94,6 +98,11 @@
}
@Override
+ public void setOnLogoClickListener(Runnable listener) {
+ mToolbarController.setOnLogoClickListener(listener);
+ }
+
+ @Override
public void setSearchHint(String s) {
mToolbarController.setSearchHint(s);
}
@@ -169,15 +178,13 @@
if (menuItemOEMV1.isCheckable()) menuItemBuilder.setCheckable();
menuItemBuilder
- .setActivated(menuItemOEMV1.isActivated())
- .setChecked(menuItemOEMV1.isChecked())
.setEnabled(menuItemOEMV1.isEnabled())
.setVisible(menuItemOEMV1.isVisible())
.setPrimary(menuItemOEMV1.isPrimary())
.setTinted(menuItemOEMV1.isTinted())
.setTitle(menuItemOEMV1.getTitle())
.setIcon(menuItemOEMV1.getIcon())
- .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 1
+ .setDisplayBehavior(menuItemOEMV1.getDisplayBehavior() == 0
? MenuItem.DisplayBehavior.ALWAYS : MenuItem.DisplayBehavior.NEVER)
.setShowIconAndTitle(menuItemOEMV1.isShowingIconAndTitle())
.setOnClickListener(menuItem -> {
@@ -187,12 +194,26 @@
})
.setId(menuItemOEMV1.getKey());
+ if (menuItemOEMV1.isActivatable()) {
+ menuItemBuilder.setActivatable();
+ }
+ if (menuItemOEMV1.isCheckable()) {
+ menuItemBuilder.setCheckable();
+ }
+ if (menuItemOEMV1.isActivated()) {
+ menuItemBuilder.setActivated(menuItemOEMV1.isActivated());
+ }
+ if (menuItemOEMV1.isChecked()) {
+ menuItemBuilder.setChecked(menuItemOEMV1.isChecked());
+ }
+
menuItemBuilder.setUxRestrictions(menuItemOEMV1.isRestricted()
? CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED
: CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
return menuItemBuilder.build();
}
+
@Override
public void setSearchListener(Consumer<String> consumer) {
if (consumer != null) {
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarControllerImpl.java b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarControllerImpl.java
new file mode 100644
index 0000000..b68ae2e
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/plugin/src/main/java/com/chassis/car/ui/plugin/toolbar/ToolbarControllerImpl.java
@@ -0,0 +1,1213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.chassis.car.ui.plugin.toolbar;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.car.ui.utils.CarUiUtils.findViewByRefId;
+import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.XmlRes;
+import androidx.core.content.ContextCompat;
+
+import com.android.car.ui.AlertDialogBuilder;
+import com.android.car.ui.CarUiText;
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+import com.android.car.ui.plugin.oemapis.toolbar.ImeSearchInterfaceOEMV2;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
+import com.android.car.ui.toolbar.DeprecatedTabWrapper;
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.toolbar.MenuItemXmlParserUtil;
+import com.android.car.ui.toolbar.NavButtonMode;
+import com.android.car.ui.toolbar.ProgressBarController;
+import com.android.car.ui.toolbar.SearchCapabilities;
+import com.android.car.ui.toolbar.SearchConfig;
+import com.android.car.ui.toolbar.SearchMode;
+import com.android.car.ui.toolbar.SearchView;
+import com.android.car.ui.toolbar.SearchWidescreenController;
+import com.android.car.ui.toolbar.Tab;
+import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.ToolbarController;
+import com.android.car.ui.utils.CarUiUtils;
+import com.android.car.ui.widget.CarUiTextView;
+
+import com.chassis.car.ui.plugin.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * The implementation of {@link ToolbarController}. This class takes a ViewGroup, and looks in the
+ * ViewGroup to find all the toolbar-related views to control.
+ */
+public class ToolbarControllerImpl implements ToolbarController {
+ private static final String TAG = "CarUiPortraitToolbarController";
+
+ @Nullable
+ private View mBackground;
+ private ImageView mNavIcon;
+ private ViewGroup mNavIconContainer;
+ private ViewGroup mTitleLogoContainer;
+ private ViewGroup mTitleContainer;
+ private Runnable mOnLogoClickListener;
+ private CarUiTextView mTitle;
+ @NonNull
+ private CarUiText mTitleText = new CarUiText.Builder("").build();
+ private CarUiTextView mSubtitle;
+ @NonNull
+ private CarUiText mSubtitleText = new CarUiText.Builder("").build();
+ private ImageView mLogo;
+ private TabLayout mTabLayout;
+ private ViewGroup mMenuItemsContainer;
+ private SearchConfig.SearchConfigBuilder mSearchConfigBuilder;
+ private final FrameLayout mSearchViewContainer;
+ private SearchView mSearchView;
+
+ // Cached values that we will send to views when they are inflated
+ private CharSequence mSearchHint;
+ private Drawable mSearchIcon;
+ private String mSearchQuery;
+ private final Context mContext;
+ private final Context mPluginContext;
+ private final Set<Consumer<String>> mOnSearchListeners = new HashSet<>();
+ private final Set<Runnable> mOnSearchCompletedListeners = new HashSet<>();
+ private final Set<Toolbar.OnSearchListener> mDeprecatedSearchListeners = new HashSet<>();
+ private final Set<Toolbar.OnSearchCompletedListener> mDeprecatedSearchCompletedListeners =
+ new HashSet<>();
+
+ private final Set<Toolbar.OnBackListener> mDeprecatedBackListeners = new HashSet<>();
+ private final Set<Supplier<Boolean>> mBackListeners = new HashSet<>();
+ private final Set<Toolbar.OnTabSelectedListener> mOnTabSelectedListeners = new HashSet<>();
+
+ private final MenuItem mOverflowButton;
+ private boolean mShowTabsInSubpage = false;
+ private boolean mHasLogo = false;
+ private boolean mShowMenuItemsWhileSearching;
+ private Toolbar.State mState = Toolbar.State.HOME;
+ private boolean mStateSet = false;
+ private NavButtonMode mNavButtonMode = NavButtonMode.DISABLED;
+ private SearchMode mSearchMode = SearchMode.DISABLED;
+ @NonNull
+ private List<MenuItem> mMenuItems = Collections.emptyList();
+ private List<MenuItem> mOverflowItems = new ArrayList<>();
+ private final List<CarUiListItem> mUiOverflowItems = new ArrayList<>();
+ private final CarUiListItemAdapter mOverflowAdapter;
+ private final List<MenuItemRenderer> mMenuItemRenderers = new ArrayList<>();
+ private final List<DeprecatedTabWrapper> mDeprecatedTabs = new ArrayList<>();
+ private View[] mMenuItemViews;
+ private int mMenuItemsXmlId = 0;
+ private AlertDialog mOverflowDialog;
+ private final boolean mShowLogo;
+ private SearchConfig mSearchConfigForWidescreen;
+ private final ProgressBarController mProgressBar;
+ private final View mNavIconSpacer;
+ private final View mLogoSpacer;
+ private final View mTitleSpacer;
+ private final MenuItem.Listener mOverflowItemListener = item -> {
+ updateOverflowDialog(item);
+ update();
+ };
+ private Consumer<TextView> mSearchTextViewConsumer = null;
+ private BiConsumer<String, Bundle> mOnPrivateImeCommandListener = null;
+
+
+ public ToolbarControllerImpl(View view, Context pluginContext) {
+ mContext = view.getContext();
+ mPluginContext = pluginContext;
+ mOverflowButton = MenuItem.builder(mPluginContext)
+ .setIcon(R.drawable.car_ui_icon_overflow_menu)
+ .setTitle(R.string.car_ui_toolbar_menu_item_overflow_title)
+ .setOnClickListener(v -> {
+ if (mOverflowDialog == null) {
+ if (Log.isLoggable(TAG, Log.ERROR)) {
+ Log.e(TAG, "Overflow dialog was null when trying to show it!");
+ }
+ } else {
+ mOverflowDialog.show();
+ }
+ })
+ .build();
+
+ mShowLogo = mPluginContext.getResources().getBoolean(
+ R.bool.car_ui_toolbar_show_logo);
+ mSearchHint = mPluginContext.getString(R.string.car_ui_toolbar_default_search_hint);
+
+ mBackground = findViewByRefId(view, R.id.car_ui_portrait_toolbar_background);
+ mLogoSpacer = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_logo_spacer);
+ mLogo = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_logo);
+ mOnLogoClickListener = null;
+ mTitleLogoContainer = requireViewByRefId(view,
+ R.id.car_ui_portrait_toolbar_title_logo_container);
+ mTitleSpacer = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_title_spacer);
+ mTitleContainer = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_title_container);
+ mTitle = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_title);
+ mSubtitle = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_subtitle);
+ mNavIconSpacer = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_nav_icon_spacer);
+ mNavIconContainer = requireViewByRefId(view,
+ R.id.car_ui_portrait_toolbar_nav_icon_container);
+ mNavIcon = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_nav_icon);
+ mSearchViewContainer = requireViewByRefId(view, R.id.car_ui_toolbar_search_view_container);
+ mMenuItemsContainer = requireViewByRefId(view, R.id.car_ui_toolbar_menu_items_container);
+ mProgressBar = new ProgressBarControllerImpl(
+ requireViewByRefId(view, R.id.car_ui_toolbar_progress_bar));
+ mTabLayout = requireViewByRefId(view, R.id.car_ui_portrait_toolbar_tabs);
+ mSearchConfigBuilder = SearchConfig.builder();
+
+ setBackgroundShown(true);
+
+ mOverflowAdapter = new CarUiListItemAdapter(mUiOverflowItems);
+ update();
+ }
+
+ private Context getContext() {
+ return mContext;
+ }
+
+ private Drawable getDrawable(@DrawableRes int resId) {
+ if (resId == 0) {
+ return null;
+ } else {
+ return ContextCompat.getDrawable(getContext(), resId);
+ }
+ }
+
+ /**
+ * Sets the title of the toolbar to a string resource.
+ *
+ * <p>The title may not always be shown, for example with one row layout with tabs.
+ */
+ @Override
+ public void setTitle(@StringRes int title) {
+ setTitle(title == 0 ? null : getContext().getString(title));
+ }
+
+ /**
+ * Sets the title of the toolbar to a CharSequence.
+ *
+ * <p>The title may not always be shown, for example with one row layout with tabs.
+ */
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitleText = title == null ? new CarUiText.Builder("").build() : new CarUiText.Builder(
+ title).build();
+ mTitle.setText(mTitleText);
+ update();
+ }
+
+ @Override
+ public void setTitle(CarUiText title) {
+ mTitleText = title;
+ mTitle.setText(mTitleText);
+ update();
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mTitleText.getPreferredText();
+ }
+
+ /**
+ * Sets the subtitle of the toolbar to a string resource.
+ *
+ * <p>The title may not always be shown, for example with one row layout with tabs.
+ */
+ @Override
+ public void setSubtitle(@StringRes int subTitle) {
+ setSubtitle(subTitle == 0 ? null : getContext().getString(subTitle));
+ }
+
+ /**
+ * Sets the subtitle of the toolbar to a CharSequence.
+ *
+ * <p>The title may not always be shown, for example with one row layout with tabs.
+ */
+ @Override
+ public void setSubtitle(CharSequence subTitle) {
+ mSubtitleText = subTitle == null ? new CarUiText.Builder("").build()
+ : new CarUiText.Builder(subTitle).build();
+ mSubtitle.setText(mSubtitleText);
+ update();
+ }
+
+ @Override
+ public void setSubtitle(CarUiText text) {
+ mSubtitleText = text;
+ mSubtitle.setText(mSubtitleText);
+ update();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mSubtitleText.getPreferredText();
+ }
+
+ @Override
+ public void setTabs(@Nullable List<Tab> tabs) {
+ setTabs(tabs, 0);
+ }
+
+ @Override
+ public void setTabs(@Nullable List<Tab> tabs, int selectedTab) {
+ mDeprecatedTabs.clear();
+ setTabsInternal(tabs, selectedTab);
+ }
+
+ @Override
+ public List<Tab> getTabs() {
+ return mTabLayout.getTabs();
+ }
+
+ private void setTabsInternal(@Nullable List<Tab> tabs, int selectedTab) {
+ if (tabs == null || tabs.isEmpty()) {
+ selectedTab = -1;
+ } else if (selectedTab < 0 || selectedTab >= tabs.size()) {
+ throw new IllegalArgumentException("Tab position is invalid: " + selectedTab);
+ }
+ mTabLayout.setTabs(tabs, selectedTab);
+ update();
+ }
+
+ /**
+ * Gets the number of tabs in the toolbar. The tabs can be retrieved using
+ * {@link #getTab(int)}.
+ */
+ @Override
+ public int getTabCount() {
+ return mDeprecatedTabs.size();
+ }
+
+ @Override
+ public int getTabPosition(com.android.car.ui.toolbar.TabLayout.Tab tab) {
+ for (int i = 0; i < mDeprecatedTabs.size(); i++) {
+ if (mDeprecatedTabs.get(i).getDeprecatedTab() == tab) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void addTab(com.android.car.ui.toolbar.TabLayout.Tab newTab) {
+ mDeprecatedTabs.add(new DeprecatedTabWrapper(getContext(), newTab,
+ this::updateModernTabsFromDeprecatedOnes, (tab) -> {
+ for (Toolbar.OnTabSelectedListener listener : mOnTabSelectedListeners) {
+ listener.onTabSelected(newTab);
+ }
+ }));
+ updateModernTabsFromDeprecatedOnes();
+ }
+
+ private void updateModernTabsFromDeprecatedOnes() {
+ List<Tab> modernTabs = new ArrayList<>();
+
+ for (DeprecatedTabWrapper tab : mDeprecatedTabs) {
+ modernTabs.add(tab.getModernTab());
+ }
+
+ setTabsInternal(modernTabs, 0);
+ }
+
+ @Override
+ public void clearAllTabs() {
+ mDeprecatedTabs.clear();
+ setTabs(null);
+ }
+
+ @Override
+ public com.android.car.ui.toolbar.TabLayout.Tab getTab(int position) {
+ return mDeprecatedTabs.get(position).getDeprecatedTab();
+ }
+
+ /**
+ * Selects a tab added to this toolbar. See {@link #addTab(TabLayout.Tab)}.
+ */
+ @Override
+ public void selectTab(int position) {
+ mTabLayout.selectTab(position);
+ }
+
+ @Override
+ public int getSelectedTab() {
+ return mTabLayout.getSelectedTab();
+ }
+
+ /**
+ * Sets whether or not tabs should also be shown in the SUBPAGE {@link Toolbar.State}.
+ */
+ @Override
+ public void setShowTabsInSubpage(boolean showTabs) {
+ if (showTabs != mShowTabsInSubpage) {
+ mShowTabsInSubpage = showTabs;
+ update();
+ }
+ }
+
+ /**
+ * Gets whether or not tabs should also be shown in the SUBPAGE {@link Toolbar.State}.
+ */
+ @Override
+ public boolean getShowTabsInSubpage() {
+ return mShowTabsInSubpage;
+ }
+
+ /**
+ * Sets the logo to display in this toolbar. If navigation icon is being displayed, this logo
+ * will be displayed next to the title.
+ */
+ @Override
+ public void setLogo(@DrawableRes int resId) {
+ asyncSetLogo(resId, Runnable::run);
+ }
+
+ private void asyncSetLogo(int resId, Executor bgExecutor) {
+ if (!mShowLogo) {
+ // If no logo should be shown then we act as if we never received one.
+ return;
+ }
+ if (resId != 0) {
+ bgExecutor.execute(() -> {
+ // load resource on background thread.
+ Drawable drawable = getDrawable(resId);
+ mLogo.post(() -> {
+ // UI thread.
+ mLogo.setImageDrawable(drawable);
+ });
+ });
+ mHasLogo = true;
+ } else {
+ mHasLogo = false;
+ }
+ update();
+ }
+
+ /**
+ * Sets the logo to display in this toolbar. If navigation icon is being displayed, this logo
+ * will be displayed next to the title.
+ */
+ @Override
+ public void setLogo(Drawable drawable) {
+ if (!mShowLogo) {
+ // If no logo should be shown then we act as if we never received one.
+ return;
+ }
+ if (drawable != null) {
+ mLogo.setImageDrawable(drawable);
+ mHasLogo = true;
+ } else {
+ mHasLogo = false;
+ }
+
+ update();
+ }
+
+ @Override
+ public void setOnLogoClickListener(@Nullable Runnable listener) {
+ if (mOnLogoClickListener != listener) {
+ mOnLogoClickListener = listener;
+ update();
+ }
+ }
+
+ /**
+ * Sets the hint for the search bar.
+ */
+ @Override
+ public void setSearchHint(@StringRes int resId) {
+ setSearchHint(getContext().getString(resId));
+ }
+
+ /**
+ * Sets the hint for the search bar.
+ */
+ public void setSearchHint(CharSequence hint) {
+ mSearchHint = hint;
+ if (mSearchView != null) {
+ mSearchView.setHint(mSearchHint);
+ }
+ }
+
+ /**
+ * Gets the search hint
+ */
+ @Override
+ public CharSequence getSearchHint() {
+ return mSearchHint;
+ }
+
+ /**
+ * Sets the icon to display in the search box.
+ *
+ * <p>The icon will be lost on configuration change, make sure to set it in onCreate() or
+ * a similar place.
+ */
+ @Override
+ public void setSearchIcon(@DrawableRes int resId) {
+ setSearchIcon(getDrawable(resId));
+ }
+
+ /**
+ * Sets the icon to display in the search box.
+ *
+ * <p>The icon will be lost on configuration change, make sure to set it in onCreate() or
+ * a similar place.
+ */
+ @Override
+ public void setSearchIcon(Drawable d) {
+ if (!Objects.equals(d, mSearchIcon)) {
+ mSearchIcon = d;
+ if (mSearchView != null) {
+ mSearchView.setIcon(mSearchIcon);
+ }
+ }
+ }
+
+
+ /**
+ * Sets the {@link Toolbar.NavButtonMode}
+ */
+ @Override
+ public void setNavButtonMode(Toolbar.NavButtonMode mode) {
+ NavButtonMode modernMode;
+ switch (mode) {
+ case BACK:
+ modernMode = NavButtonMode.BACK;
+ break;
+ case DOWN:
+ modernMode = NavButtonMode.DOWN;
+ break;
+ case CLOSE:
+ modernMode = NavButtonMode.CLOSE;
+ break;
+ case DISABLED:
+ default:
+ modernMode = NavButtonMode.DISABLED;
+ break;
+ }
+ setNavButtonMode(modernMode);
+ }
+
+ @Override
+ public void setNavButtonMode(NavButtonMode mode) {
+ if (mode != mNavButtonMode) {
+ mNavButtonMode = mode;
+ update();
+ }
+ }
+
+ /**
+ * Gets the {@link Toolbar.NavButtonMode}
+ */
+ @Override
+ public NavButtonMode getNavButtonMode() {
+ return mNavButtonMode;
+ }
+
+ /**
+ * Show/hide the background. When hidden, the toolbar is completely transparent.
+ */
+ @Override
+ public void setBackgroundShown(boolean shown) {
+ if (mBackground == null) {
+ return;
+ }
+
+ if (shown) {
+ mBackground.setBackground(getDrawable(R.drawable.car_ui_portrait_toolbar_background));
+ } else {
+ mBackground.setBackground(null);
+ }
+ }
+
+ /**
+ * Returns true is the toolbar background is shown
+ */
+ @Override
+ public boolean getBackgroundShown() {
+ if (mBackground == null) {
+ return true;
+ }
+
+ return mBackground.getBackground() != null;
+ }
+
+ private void setMenuItemsInternal(@Nullable List<MenuItem> items) {
+ if (items == null) {
+ items = Collections.emptyList();
+ }
+
+ List<MenuItem> visibleMenuItems = new ArrayList<>();
+ List<MenuItem> overflowItems = new ArrayList<>();
+ AtomicInteger loadedMenuItems = new AtomicInteger(0);
+
+ synchronized (this) {
+ if (items.equals(mMenuItems)) {
+ return;
+ }
+
+ for (MenuItem item : items) {
+ if (item.getDisplayBehavior() == MenuItem.DisplayBehavior.NEVER) {
+ overflowItems.add(item);
+ item.setListener(mOverflowItemListener);
+ } else {
+ visibleMenuItems.add(item);
+ }
+ }
+
+ // Copy the list so that if the list is modified and setMenuItems is called again,
+ // the equals() check will fail. Note that the MenuItems are not copied here.
+ mMenuItems = new ArrayList<>(items);
+ mOverflowItems = overflowItems;
+ mMenuItemRenderers.clear();
+ mMenuItemsContainer.removeAllViews();
+
+ if (!overflowItems.isEmpty()) {
+ visibleMenuItems.add(mOverflowButton);
+ createOverflowDialog();
+ }
+
+ View[] menuItemViews = new View[visibleMenuItems.size()];
+ mMenuItemViews = menuItemViews;
+
+ for (int i = 0; i < visibleMenuItems.size(); ++i) {
+ int index = i;
+ MenuItem item = visibleMenuItems.get(i);
+ MenuItemRenderer renderer = new MenuItemRenderer(item, mMenuItemsContainer);
+ mMenuItemRenderers.add(renderer);
+ renderer.createView(view -> {
+ synchronized (ToolbarControllerImpl.this) {
+ if (menuItemViews != mMenuItemViews) {
+ return;
+ }
+
+ menuItemViews[index] = view;
+ if (loadedMenuItems.addAndGet(1) == menuItemViews.length) {
+ for (View v : menuItemViews) {
+ mMenuItemsContainer.addView(v);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ update();
+ }
+
+ /**
+ * Sets the {@link MenuItem Menuitems} to display.
+ */
+ @Override
+ public void setMenuItems(@Nullable List<MenuItem> items) {
+ mMenuItemsXmlId = 0;
+ setMenuItemsInternal(items);
+ }
+
+ /**
+ * Sets the {@link MenuItem Menuitems} to display to a list defined in XML.
+ *
+ * <p>If this method is called twice with the same argument (and {@link #setMenuItems(List)}
+ * wasn't called), nothing will happen the second time, even if the MenuItems were changed.
+ *
+ * <p>The XML file must have one <MenuItems> tag, with a variable number of <MenuItem>
+ * child tags. See CarUiToolbarMenuItem in CarUi's attrs.xml for a list of available
+ * attributes.
+ * <p>
+ * Example:
+ * <pre>
+ * <MenuItems>
+ * <MenuItem
+ * app:title="Foo"/>
+ * <MenuItem
+ * app:title="Bar"
+ * app:icon="@drawable/ic_tracklist"
+ * app:onClick="xmlMenuItemClicked"/>
+ * <MenuItem
+ * app:title="Bar"
+ * app:checkable="true"
+ * app:uxRestrictions="FULLY_RESTRICTED"
+ * app:onClick="xmlMenuItemClicked"/>
+ * </MenuItems>
+ * </pre>
+ *
+ * @return The MenuItems that were loaded from XML.
+ * @see #setMenuItems(List)
+ */
+ @Override
+ public List<MenuItem> setMenuItems(@XmlRes int resId) {
+ if (mMenuItemsXmlId != 0 && mMenuItemsXmlId == resId) {
+ return mMenuItems;
+ }
+
+ mMenuItemsXmlId = resId;
+ List<MenuItem> menuItems = MenuItemXmlParserUtil.readMenuItemList(getContext(), resId);
+ setMenuItemsInternal(menuItems);
+ return menuItems;
+ }
+
+ /**
+ * Gets the {@link MenuItem MenuItems} currently displayed
+ */
+ @Override
+ @NonNull
+ public List<MenuItem> getMenuItems() {
+ return Collections.unmodifiableList(mMenuItems);
+ }
+
+ /**
+ * Gets a {@link MenuItem} by id.
+ */
+ @Override
+ @Nullable
+ public MenuItem findMenuItemById(int id) {
+ for (MenuItem item : mMenuItems) {
+ if (item.getId() == id) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a {@link MenuItem} by id. Will throw an IllegalArgumentException if not found.
+ */
+ @Override
+ @NonNull
+ public MenuItem requireMenuItemById(int id) {
+ MenuItem result = findMenuItemById(id);
+
+ if (result == null) {
+ throw new IllegalArgumentException("ID does not reference a MenuItem on this Toolbar");
+ }
+
+ return result;
+ }
+
+ private int countVisibleOverflowItems() {
+ int numVisibleItems = 0;
+ for (MenuItem item : mOverflowItems) {
+ if (item.isVisible()) {
+ numVisibleItems++;
+ }
+ }
+ return numVisibleItems;
+ }
+
+ private void createOverflowDialog() {
+ // Need to check if overflow dialog is showing before the new AlertDialog is created
+ // because it will return false when checked after
+ boolean isShowing = mOverflowDialog == null ? false : mOverflowDialog.isShowing();
+
+ mUiOverflowItems.clear();
+ for (MenuItem menuItem : mOverflowItems) {
+ if (menuItem.isVisible()) {
+ mUiOverflowItems.add(toCarUiContentListItem(menuItem));
+ }
+ }
+ mOverflowDialog = new AlertDialogBuilder(getContext())
+ .setAdapter(mOverflowAdapter)
+ .create();
+
+ // When show() is called on a dialog, it is created from scratch. This means the underlying
+ // list of the dialog is instantiated and the corresponding adapter is set on it. So, any
+ // changes to the data of the dialog's list's adapter prior to the call to show() will be
+ // be shown on screen. Previously, if the dialog was being shown and the data of the adapter
+ // was changed (i.e., setMenuItems -> setMenuItemsInternal -> createOverflowDialog), the
+ // data of the adapter would change without show() being called, causing the updated data to
+ // not be reflected on screen. So, call notifyDataSetChanged if the dialog is being shown.
+ if (isShowing) {
+ mOverflowAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private void updateOverflowDialog(MenuItem changedItem) {
+ int itemIndex = mOverflowItems.indexOf(changedItem);
+ if (itemIndex >= 0) {
+ mUiOverflowItems.set(itemIndex, toCarUiContentListItem(changedItem));
+ mOverflowAdapter.notifyItemChanged(itemIndex);
+ } else {
+ createOverflowDialog();
+ }
+ }
+
+ private CarUiContentListItem toCarUiContentListItem(MenuItem menuItem) {
+ CarUiContentListItem carUiItem;
+ if (menuItem.isCheckable()) {
+ carUiItem = new CarUiContentListItem(CarUiContentListItem.Action.SWITCH);
+ } else {
+ carUiItem = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ }
+ carUiItem.setIcon(menuItem.getIcon());
+ carUiItem.setActivated(menuItem.isActivated());
+ carUiItem.setChecked(menuItem.isChecked());
+ carUiItem.setEnabled(menuItem.isEnabled());
+ carUiItem.setTitle(menuItem.getTitle());
+ carUiItem.setOnItemClickedListener(item -> {
+ menuItem.performClick();
+ mOverflowDialog.hide();
+ });
+ return carUiItem;
+ }
+
+ /**
+ * Set whether or not to show the {@link MenuItem MenuItems} while searching. Default false.
+ * Even if this is set to true, the {@link MenuItem} created by
+ * {@link MenuItem.Builder#setToSearch()} will still be hidden.
+ */
+ @Override
+ public void setShowMenuItemsWhileSearching(boolean showMenuItems) {
+ mShowMenuItemsWhileSearching = showMenuItems;
+ update();
+ }
+
+ /**
+ * Returns if {@link MenuItem MenuItems} are shown while searching
+ */
+ @Override
+ public boolean getShowMenuItemsWhileSearching() {
+ return mShowMenuItemsWhileSearching;
+ }
+
+ /**
+ * Sets the search query.
+ */
+ @Override
+ public void setSearchQuery(String query) {
+ if (mSearchView != null) {
+ mSearchView.setSearchQuery(query);
+ } else {
+ mSearchQuery = query;
+ for (Toolbar.OnSearchListener listener : mDeprecatedSearchListeners) {
+ listener.onSearch(query);
+ }
+ for (Consumer<String> listener : mOnSearchListeners) {
+ listener.accept(query);
+ }
+ }
+ }
+
+ /**
+ * Sets the state of the toolbar. This will show/hide the appropriate elements of the toolbar
+ * for the desired state.
+ */
+ @Override
+ public void setState(Toolbar.State state) {
+ if (mState != state || !mStateSet) {
+ mState = state;
+ mStateSet = true;
+ update();
+ }
+ }
+
+ @Override
+ public void setSearchMode(SearchMode mode) {
+ if (mStateSet) {
+ throw new IllegalStateException("Cannot set search mode when using setState()");
+ }
+ if (mSearchMode != mode) {
+ mSearchMode = mode;
+ update();
+ }
+ }
+
+ @Override
+ public SearchMode getSearchMode() {
+ return mSearchMode;
+ }
+
+ private void update() {
+ // Start by removing mState/mStateSet from the equation by incorporating them into other
+ // variables.
+ NavButtonMode navButtonMode = mNavButtonMode;
+ if (mStateSet) {
+ if (mState == Toolbar.State.HOME) {
+ navButtonMode = NavButtonMode.DISABLED;
+ } else if (navButtonMode == NavButtonMode.DISABLED) {
+ navButtonMode = NavButtonMode.BACK;
+ }
+ }
+
+ SearchMode searchMode = mSearchMode;
+ if (mStateSet) {
+ if (mState == Toolbar.State.SEARCH) {
+ searchMode = SearchMode.SEARCH;
+ } else if (mState == Toolbar.State.EDIT) {
+ searchMode = SearchMode.EDIT;
+ } else {
+ searchMode = SearchMode.DISABLED;
+ }
+ }
+
+ boolean hasLogo = mHasLogo;
+ if (mStateSet && (mState == Toolbar.State.SEARCH || mState == Toolbar.State.EDIT)) {
+ hasLogo = false;
+ }
+
+ boolean hasTabs = mTabLayout.hasTabs();
+ if (mStateSet && mState != Toolbar.State.HOME
+ && !(mState == Toolbar.State.SUBPAGE && mShowTabsInSubpage)) {
+ hasTabs = false;
+ }
+
+ boolean isSearching = searchMode != SearchMode.DISABLED;
+ if (mSearchView == null && isSearching) {
+ inflateSearchView();
+ }
+
+ for (MenuItemRenderer renderer : mMenuItemRenderers) {
+ renderer.setToolbarIsSearching(searchMode == SearchMode.SEARCH);
+ }
+
+ View.OnClickListener backClickListener = (v) -> {
+ boolean absorbed = false;
+ List<Toolbar.OnBackListener> deprecatedListenersCopy =
+ new ArrayList<>(mDeprecatedBackListeners);
+ List<Supplier<Boolean>> listenersCopy = new ArrayList<>(mBackListeners);
+ for (Toolbar.OnBackListener listener : deprecatedListenersCopy) {
+ absorbed = absorbed || listener.onBack();
+ }
+ for (Supplier<Boolean> listener : listenersCopy) {
+ absorbed = absorbed || listener.get();
+ }
+
+ if (!absorbed) {
+ Activity activity = CarUiUtils.getActivity(getContext());
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+ };
+
+ switch (navButtonMode) {
+ case CLOSE:
+ mNavIcon.setImageResource(R.drawable.car_ui_icon_close);
+ break;
+ case DOWN:
+ mNavIcon.setImageResource(R.drawable.car_ui_icon_down);
+ break;
+ default:
+ mNavIcon.setImageResource(R.drawable.car_ui_icon_arrow_back);
+ break;
+ }
+
+ mLogoSpacer.setVisibility(
+ !hasTabs && navButtonMode != NavButtonMode.DISABLED ? VISIBLE : GONE);
+ mLogo.setVisibility(hasLogo ? VISIBLE : GONE);
+
+ mTitleSpacer.setVisibility(
+ hasTabs && navButtonMode != NavButtonMode.DISABLED ? VISIBLE : GONE);
+ mTitleLogoContainer.setOnClickListener(
+ hasLogo && mOnLogoClickListener != null && navButtonMode != NavButtonMode.DISABLED
+ ? v -> mOnLogoClickListener.run() : null);
+ mTitleLogoContainer.setVisibility(isSearching ? GONE : VISIBLE);
+
+ // Show the nav icon container if we're not in the home space or the logo fills the nav icon
+ // container. If car_ui_toolbar_nav_icon_reserve_space is true, hiding it will still reserve
+ // its space
+ mNavIconContainer.setVisibility(navButtonMode != NavButtonMode.DISABLED ? VISIBLE : GONE);
+ mNavIconContainer.setOnClickListener(
+ navButtonMode != NavButtonMode.DISABLED ? backClickListener : null);
+ mNavIconContainer.setClickable(navButtonMode != NavButtonMode.DISABLED);
+ mNavIconContainer.setContentDescription(navButtonMode != NavButtonMode.DISABLED
+ ? getContext().getString(R.string.car_ui_toolbar_nav_icon_content_description)
+ : null);
+ mNavIconSpacer.setVisibility(hasTabs && !isSearching ? VISIBLE : GONE);
+
+ // Show the title if we're in the subpage state, or in the home state with no tabs or tabs
+ // on the second row
+ mSubtitle.setVisibility(
+ TextUtils.isEmpty(getSubtitle()) ? GONE : VISIBLE);
+
+ mTabLayout.setVisibility(hasTabs && !isSearching ? VISIBLE : GONE);
+
+ if (mSearchView != null) {
+ if (isSearching) {
+ mSearchView.setPlainText(searchMode == SearchMode.EDIT);
+ mSearchView.setVisibility(VISIBLE);
+ } else {
+ mSearchView.setVisibility(GONE);
+ }
+ }
+
+ boolean showButtons = !isSearching || mShowMenuItemsWhileSearching;
+ mMenuItemsContainer.setVisibility(showButtons ? VISIBLE : GONE);
+ mOverflowButton.setVisible(showButtons && countVisibleOverflowItems() > 0);
+ }
+
+ private void inflateSearchView() {
+ SearchView searchView = new SearchView(mPluginContext);
+ searchView.setHint(mSearchHint);
+ searchView.setIcon(mSearchIcon);
+ searchView.setSearchQuery(mSearchQuery);
+ searchView.setSearchListeners(
+ mDeprecatedSearchListeners, mOnSearchListeners);
+ searchView.setSearchCompletedListeners(
+ mDeprecatedSearchCompletedListeners, mOnSearchCompletedListeners);
+ searchView.setVisibility(GONE);
+
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mSearchViewContainer.addView(searchView, layoutParams);
+
+ searchView.setSearchConfig(mSearchConfigForWidescreen);
+
+ mSearchView = searchView;
+
+ // These consumers should only be set once search view has been inflated
+ if (mSearchTextViewConsumer != null) {
+ mSearchView.setSearchTextViewConsumer(
+ (TextView tv) -> mSearchTextViewConsumer.accept(tv));
+ }
+ if (mOnPrivateImeCommandListener != null) {
+ mSearchView.setOnPrivateImeCommandListener(
+ (String s, Bundle b) -> mOnPrivateImeCommandListener.accept(s, b));
+ }
+ }
+
+ /**
+ * Add a view within a container that will animate with the wide screen IME to display search
+ * results.
+ *
+ * <p>Note: Apps can only call this method if the package name is allowed via OEM to render
+ * their view. To check if the application have the permission to do so or not first call
+ * {@link SearchCapabilities#canShowSearchResultsView()}. If the app is not allowed this method
+ * will throw an {@link IllegalStateException}
+ *
+ * @param view to be added in the container.
+ */
+ @Override
+ public void setSearchResultsView(View view) {
+ if (!getSearchCapabilities().canShowSearchResultsView()) {
+ throw new IllegalStateException(
+ "not allowed to add view to wide screen IME, package name: "
+ + getContext().getPackageName());
+ }
+
+ setSearchConfig(mSearchConfigBuilder.setSearchResultsView(view).build());
+ }
+
+ @Override
+ public void setSearchResultsInputViewIcon(@NonNull Drawable drawable) {
+ setSearchConfig(mSearchConfigBuilder.setSearchResultsInputViewIcon(drawable).build());
+ }
+
+ /**
+ * Sets list of search item {@link CarUiListItem} to be displayed in the IMS template. This
+ * method should be called when system is running in a wide screen mode. Apps can check that by
+ * using {@link SearchCapabilities#canShowSearchResultItems()} Else, this method will throw an
+ * {@link IllegalStateException}
+ */
+ @Override
+ public void setSearchResultItems(List<? extends CarUiImeSearchListItem> searchItems) {
+ if (!getSearchCapabilities().canShowSearchResultItems()) {
+ throw new IllegalStateException(
+ "system not in wide screen mode, not allowed to set search result items ");
+ }
+
+ setSearchConfig(mSearchConfigBuilder.setSearchResultItems(searchItems).build());
+ }
+
+ @Override
+ public void setSearchConfig(SearchConfig searchConfig) {
+ mSearchConfigForWidescreen = searchConfig;
+ if (mSearchView != null) {
+ mSearchView.setSearchConfig(mSearchConfigForWidescreen);
+ }
+ }
+
+ @Override
+ public SearchCapabilities getSearchCapabilities() {
+ return SearchWidescreenController.getSearchCapabilities(mContext);
+ }
+
+ @Override
+ public boolean canShowSearchResultItems() {
+ return getSearchCapabilities().canShowSearchResultItems();
+ }
+
+ @Override
+ public boolean canShowSearchResultsView() {
+ return getSearchCapabilities().canShowSearchResultsView();
+ }
+
+ /**
+ * Gets the current {@link Toolbar.State} of the toolbar.
+ */
+ @Override
+ public Toolbar.State getState() {
+ return mState;
+ }
+
+ /**
+ * Returns whether or not the state of the toolbar was previously set.
+ */
+ @Override
+ public boolean isStateSet() {
+ return mStateSet;
+ }
+
+ /**
+ * Registers a new {@link Toolbar.OnTabSelectedListener} to the list of listeners.
+ */
+ @Override
+ public void registerOnTabSelectedListener(Toolbar.OnTabSelectedListener listener) {
+ mOnTabSelectedListeners.add(listener);
+ }
+
+ /**
+ * Unregisters an existing {@link Toolbar.OnTabSelectedListener} from the list of listeners.
+ */
+ @Override
+ public boolean unregisterOnTabSelectedListener(Toolbar.OnTabSelectedListener listener) {
+ return mOnTabSelectedListeners.remove(listener);
+ }
+
+ /**
+ * Registers a new {@link Toolbar.OnSearchListener} to the list of listeners.
+ */
+ @Override
+ public void registerOnSearchListener(Toolbar.OnSearchListener listener) {
+ mDeprecatedSearchListeners.add(listener);
+ }
+
+ /**
+ * Unregisters an existing {@link Toolbar.OnSearchListener} from the list of listeners.
+ */
+ @Override
+ public boolean unregisterOnSearchListener(Toolbar.OnSearchListener listener) {
+ return mDeprecatedSearchListeners.remove(listener);
+ }
+
+ @Override
+ public void registerSearchListener(Consumer<String> listener) {
+ mOnSearchListeners.add(listener);
+ }
+
+ @Override
+ public boolean unregisterSearchListener(Consumer<String> listener) {
+ return mOnSearchListeners.remove(listener);
+ }
+
+ /**
+ * Registers a new {@link Toolbar.OnSearchCompletedListener} to the list of listeners.
+ */
+ @Override
+ public void registerOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener) {
+ mDeprecatedSearchCompletedListeners.add(listener);
+ }
+
+ /**
+ * Unregisters an existing {@link Toolbar.OnSearchCompletedListener} from the list of
+ * listeners.
+ */
+ @Override
+ public boolean unregisterOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener) {
+ return mDeprecatedSearchCompletedListeners.remove(listener);
+ }
+
+ @Override
+ public void registerSearchCompletedListener(Runnable listener) {
+ mOnSearchCompletedListeners.add(listener);
+ }
+
+ @Override
+ public boolean unregisterSearchCompletedListener(Runnable listener) {
+ return mOnSearchCompletedListeners.remove(listener);
+ }
+
+ /**
+ * Registers a new {@link Toolbar.OnBackListener} to the list of listeners.
+ */
+ @Override
+ public void registerOnBackListener(Toolbar.OnBackListener listener) {
+ mDeprecatedBackListeners.add(listener);
+ }
+
+ /**
+ * Unregisters an existing {@link Toolbar.OnBackListener} from the list of listeners.
+ */
+ @Override
+ public boolean unregisterOnBackListener(Toolbar.OnBackListener listener) {
+ return mDeprecatedBackListeners.remove(listener);
+ }
+
+ @Override
+ public void registerBackListener(Supplier<Boolean> listener) {
+ mBackListeners.add(listener);
+ }
+
+ @Override
+ public boolean unregisterBackListener(Supplier<Boolean> listener) {
+ return mBackListeners.remove(listener);
+ }
+
+ /**
+ * Returns the progress bar.
+ */
+ @Override
+ public ProgressBarController getProgressBar() {
+ return mProgressBar;
+ }
+
+ /**
+ * Returns a ImeSearchInterfaceOEMV2 implementation.
+ */
+ public ImeSearchInterfaceOEMV2 getImeSearchInterface() {
+ return new ImeSearchInterfaceOEMV2() {
+ @Override
+ public void setSearchTextViewConsumer(
+ @Nullable com.android.car.ui.plugin.oemapis.Consumer<TextView> consumer) {
+ mSearchTextViewConsumer = (TextView tv) -> consumer.accept(tv);
+ }
+
+ @Override
+ public void setOnPrivateImeCommandListener(@Nullable
+ com.android.car.ui.plugin.oemapis.BiConsumer<String, Bundle> biConsumer) {
+ mOnPrivateImeCommandListener = (String s, Bundle b) -> biConsumer.accept(s, b);
+ }
+ };
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/Android.bp b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/Android.bp
index 19d9eb0..158e974 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/Android.bp
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/Android.bp
@@ -36,6 +36,7 @@
"androidx-constraintlayout_constraintlayout-solver",
"androidx-constraintlayout_constraintlayout",
"androidx.lifecycle_lifecycle-extensions",
+ "androidx.car.app_app",
"car-ui-lib",
"CarLauncher-core",
"WindowManager-Shell",
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/AndroidManifest.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/AndroidManifest.xml
index 16194cb..0351c3c 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/AndroidManifest.xml
@@ -30,6 +30,7 @@
android:label="@string/app_title"
android:theme="@style/Theme.CarUi.NoToolbar"
tools:replace="android:label,android:theme"
+ android:directBootAware="true"
tools:node="merge">
<activity
@@ -43,7 +44,7 @@
</activity>
<activity
- android:name=".homeactivities.BlankActivity"
+ android:name=".homeactivities.BackgroundPanelBaseActivity"
android:exported="true"
android:launchMode="singleTask"
android:excludeFromRecents="true">
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-night/base_layer_wallpaper.png b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-night/base_layer_wallpaper.png
new file mode 100644
index 0000000..0c9f378
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-night/base_layer_wallpaper.png
Binary files differ
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/control_bar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/control_bar_background.xml
deleted file mode 100644
index dc1cade..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/control_bar_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><!--
- ~ Copyright (C) 2022 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.
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:height="@dimen/corner_radius"
- android:gravity="bottom">
- <shape>
- <solid android:color="@color/car_neutral_0" />
- </shape>
- </item>
-</layer-list>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/app_icon_background.xml
similarity index 74%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/app_icon_background.xml
index 8ad0860..86b5540 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/app_icon_background.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/car_surface_2"/>
+</shape>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/base_layer_wallpaper.png b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/base_layer_wallpaper.png
new file mode 100644
index 0000000..b6b9214
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/base_layer_wallpaper.png
Binary files differ
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/control_bar_background.xml
similarity index 74%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/control_bar_background.xml
index 5564d8e..912b0ba 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/control_bar_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?><!--
+ ~ Copyright (C) 2022 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.
@@ -18,17 +17,17 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
- <solid android:color="@color/car_background"/>
+ <solid android:color="@color/car_panel_border"/>
<corners android:topLeftRadius="@dimen/corner_radius"
android:topRightRadius="@dimen/corner_radius"
android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
</shape>
</item>
- <item android:height="@dimen/grip_bar_height" android:width="@dimen/grip_bar_width" android:gravity="center">
+ <item
+ android:height="@dimen/corner_radius"
+ android:gravity="bottom">
<shape>
- <solid android:color="@color/car_surface_variant"/>
+ <solid android:color="@color/car_neutral_0" />
</shape>
</item>
</layer-list>
-
-
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/grip_bar_background.xml
similarity index 74%
copy from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/grip_bar_background.xml
index 5564d8e..fce5532 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/grip_bar_background.xml
@@ -18,13 +18,22 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
+ <solid android:color="@color/car_panel_border"/>
+ <corners android:topLeftRadius="@dimen/corner_radius"
+ android:topRightRadius="@dimen/corner_radius"
+ android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
+ </shape>
+ </item>
+ <item android:top="@dimen/car_panel_border_width">
+ <shape>
<solid android:color="@color/car_background"/>
<corners android:topLeftRadius="@dimen/corner_radius"
android:topRightRadius="@dimen/corner_radius"
android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
</shape>
</item>
- <item android:height="@dimen/grip_bar_height" android:width="@dimen/grip_bar_width" android:gravity="center">
+ <item android:height="@dimen/grip_bar_height" android:width="@dimen/grip_bar_width"
+ android:gravity="center">
<shape>
<solid android:color="@color/car_surface_variant"/>
</shape>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth.xml
new file mode 100644
index 0000000..a0e475d
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M35.42,15.42L24,4H22V19.18L12.82,10L10,12.82L21.18,24L10,35.18L12.82,38L22,28.82V44H24L35.42,32.58L26.82,24L35.42,15.42ZM26,11.66L29.76,15.42L26,19.18V11.66ZM26,36.34L29.76,32.58L26,28.82V36.34ZM10,27C11.657,27 13,25.657 13,24C13,22.343 11.657,21 10,21C8.343,21 7,22.343 7,24C7,25.657 8.343,27 10,27ZM41,24C41,25.657 39.657,27 38,27C36.343,27 35,25.657 35,24C35,22.343 36.343,21 38,21C39.657,21 41,22.343 41,24Z"
+ android:fillColor="@color/car_on_surface"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth_activatable.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth_activatable.xml
new file mode 100644
index 0000000..a89b9f0
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_bluetooth_activatable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:width="@dimen/button_tap_target_size"
+ android:height="@dimen/button_tap_target_size"
+ android:drawable="@drawable/dialer_button_active_state_circle"/>
+
+ <item android:width="@dimen/home_card_button_size"
+ android:height="@dimen/home_card_button_size"
+ android:gravity="center"
+ android:tint="@color/dialer_icon_tint_state_list"
+ android:drawable="@drawable/ic_bluetooth"/>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone.xml
new file mode 100644
index 0000000..e2bbbaf
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M14,2L34,2.02C36.2,2.02 38,3.8 38,6V42C38,44.2 36.2,46 34,46H14C11.8,46 10,44.2 10,42V6C10,3.8 11.8,2 14,2ZM14,42H34V40H14V42ZM34,36H14V12H34V36ZM14,6V8H34V6H14Z"
+ android:fillColor="@color/car_on_surface"
+ android:fillType="evenOdd"/>
+</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone_activatable.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone_activatable.xml
new file mode 100644
index 0000000..1660c59
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_smartphone_activatable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:width="@dimen/button_tap_target_size"
+ android:height="@dimen/button_tap_target_size"
+ android:drawable="@drawable/dialer_button_active_state_circle"/>
+
+ <item android:width="@dimen/home_card_button_size"
+ android:height="@dimen/home_card_button_size"
+ android:gravity="center"
+ android:tint="@color/dialer_icon_tint_state_list"
+ android:drawable="@drawable/ic_smartphone"/>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker.xml
new file mode 100644
index 0000000..02a4012
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M4.25,4.21C6.24,2.23 8.98,1 12,1C15.02,1 17.76,2.23 19.75,4.21L18.34,5.62C16.72,4 14.47,3 12,3C9.53,3 7.28,4 5.66,5.62L4.25,4.21ZM8.43,8.5L7,7.07C8.28,5.79 10.05,5 12,5C13.95,5 15.72,5.79 17,7.07L15.57,8.5C14.66,7.59 13.39,7.02 12,7.02C10.61,7.02 9.34,7.59 8.43,8.5ZM10,9.99C8.9,9.99 8,10.89 8,11.99V19.99C8,21.09 8.9,21.99 10,21.99H14C15.1,21.99 16,21.09 16,19.99V11.99C16,10.89 15.1,9.99 14,9.99H10ZM10,11.99V19.99H14V11.99H10Z"
+ android:fillColor="@color/car_on_surface"
+ android:fillType="evenOdd"/>
+</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker_activatable.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker_activatable.xml
new file mode 100644
index 0000000..d61ad70
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/ic_speaker_activatable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:width="@dimen/button_tap_target_size"
+ android:height="@dimen/button_tap_target_size"
+ android:drawable="@drawable/dialer_button_active_state_circle"/>
+
+ <item android:width="@dimen/home_card_button_size"
+ android:height="@dimen/home_card_button_size"
+ android:gravity="center"
+ android:tint="@color/dialer_icon_tint_state_list"
+ android:drawable="@drawable/ic_speaker"/>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/immersive_back.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/immersive_back.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/immersive_back.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable/immersive_back.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_audio_card.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_audio_card.xml
new file mode 100644
index 0000000..0c6c092
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_audio_card.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/in_call_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@+id/media_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_background_panel_base.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_background_panel_base.xml
new file mode 100644
index 0000000..af4d912
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_background_panel_base.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?><!--
+ ~ Copyright (C) 2023 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.
+ -->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/base_layer_wallpaper">
+
+ <TextView
+ android:id="@+id/base_layer_failure_recovery_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/base_layer_failure_recovery_title_string"
+ android:textAppearance="@style/TextAppearance.Car.Display"
+ android:textColor="@color/car_on_surface"
+ app:layout_constraintVertical_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/base_layer_failure_recovery_message"/>
+
+ <TextView
+ android:id="@+id/base_layer_failure_recovery_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/base_layer_failure_recovery_message_string"
+ android:textAppearance="@style/TextAppearance.Car.Body.Medium"
+ android:textColor="@color/car_on_surface"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/base_layer_failure_recovery_title"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_grip_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_grip_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_grip_bar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_grip_bar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_launcher.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_launcher.xml
similarity index 86%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_launcher.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_launcher.xml
index b4d6535..5ff996c 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_launcher.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_launcher.xml
@@ -19,7 +19,9 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/car_under_surface">
+ android:background="@color/car_under_surface"
+ android:clipToPadding="false"
+ android:clipChildren="false">
<FrameLayout
android:id="@+id/background_app_area"
@@ -34,13 +36,15 @@
android:id="@+id/control_bar_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingTop="@dimen/car_panel_border_width"
android:background="@drawable/control_bar_background"
android:layout_gravity="bottom"
android:gravity="bottom"
+ android:elevation="@dimen/panel_elevation"
android:orientation="vertical">
<FrameLayout
- android:id="@+id/bottom_card"
+ android:id="@+id/control_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
@@ -49,4 +53,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fullscreen_container"/>
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_panel.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel.xml
similarity index 92%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_panel.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel.xml
index 4781fdf..becbadf 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_panel.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel.xml
@@ -42,11 +42,10 @@
android:layout_below="@id/grip_bar"
android:background="@color/car_background"/>
- <FrameLayout
- android:id="@+id/task_view_overlay"
+ <include
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/grip_bar"
- android:background="@color/car_background"
- android:visibility="gone"/>
+ android:visibility="gone"
+ layout="@layout/car_ui_portrait_panel_overlay"/>`
</com.android.car.portraitlauncher.panel.TaskViewPanel>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel_overlay.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel_overlay.xml
new file mode 100644
index 0000000..c8052e5
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_panel_overlay.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8" ?><!--
+ ~ Copyright (C) 2023 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.
+ -->
+<com.android.car.portraitlauncher.panel.TaskViewPanelOverlay
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/task_view_overlay"
+ android:background="@color/car_background">
+
+ <ImageView
+ android:id="@+id/task_view_overlay_icon_background"
+ android:layout_width="@dimen/overlay_background_icon_size"
+ android:layout_height="@dimen/overlay_background_icon_size"
+ android:background="@drawable/app_icon_background"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+ <ImageView
+ android:id="@+id/task_view_overlay_icon"
+ android:layout_width="@dimen/overlay_icon_size"
+ android:layout_height="@dimen/overlay_icon_size"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+</com.android.car.portraitlauncher.panel.TaskViewPanelOverlay>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_toolbar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_toolbar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout-port/car_ui_portrait_toolbar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/layout/car_ui_portrait_toolbar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/config.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/config.xml
similarity index 64%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/config.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/config.xml
index 2f77cc6..61e268a 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/config.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/config.xml
@@ -18,7 +18,7 @@
<resources>
<string-array name="config_backgroundActivities" translatable="false">
<item>com.google.android.apps.maps/com.google.android.maps.MapsActivity</item>
- <item>com.waze/androidx.car.app.activity.CarAppActivity</item>
+ <item>com.android.car.portraitlauncher/com.android.car.portraitlauncher.homeactivities.BackgroundPanelBaseActivity</item>
</string-array>
<string name="config_notificationActivity" translatable="false">
@@ -42,7 +42,7 @@
<!-- A list of package names that provide the cards to display on the home screen -->
<string-array name="config_homeCardModuleClasses" translatable="false">
- <item>com.android.car.portraitlauncher.homescreen.audio.PortraitAudioCard</item>
+ <item>com.android.car.portraitlauncher.controlbar.AudioCardModule</item>
</string-array>
<!--
@@ -58,8 +58,27 @@
<item>android.widget.cts/android.widget.cts.AdapterViewCtsActivity</item>
<item>android.widget.cts/android.widget.cts.EditTextCtsActivity</item>
<item>android.sdksandbox.webkit.cts/android.app.sdksandbox.testutils.testscenario.SdkSandboxCtsActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MaxAspectRatioActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MaxAspectRatioResizableActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MaxAspectRatioUnsetActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MetaDataMaxAspectRatioActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MinAspectRatioActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MinAspectRatioLandscapeActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MinAspectRatioPortraitActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.MinAspectRatioUnsetActivity</item>
<item>android.view.surfacecontrol.cts/android.view.cts.surfacevalidator.CapturedActivity</item>
<item>android.view.surfacecontrol.cts/.TrustedPresentationCallbackTest$TestActivity</item>
<item>android.view.surfacecontrol.cts/android.view.cts.surfacevalidator.CapturedActivityWithResource</item>
+ <item>android.uirendering.cts/android.uirendering.cts.testinfrastructure.DrawActivity</item>
+ <item>android.display.cts/android.display.cts.DisplayManagerTest$TestActivity</item>
+ <!-- TODO(b/297060765): Remove AssistantActivity & AnimationTestActivity once find a proper
+ solution on AssistantStackTests test. The tests expects AssistantActivity to be
+ hidden when AnimationTestActivity shows. Without this assignment, the two activities
+ will be in different tasks, which will cause the test to fail.-->
+ <item>android.server.wm.app/android.server.wm.app.AssistantActivity</item>
+ <item>android.server.wm.app/android.server.wm.app.AnimationTestActivity</item>
+ <!-- TODO(b/297060765): TestActivity is expected to be at the top of the stack when
+ AssistantActivity hide. Put it in fullscreen panel to enforce it. -->
+ <item>android.server.wm.app/android.server.wm.app.TestActivity</item>
</string-array>
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/dimens.xml
similarity index 82%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/dimens.xml
index 3f6452b..7af4cea 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/dimens.xml
@@ -20,8 +20,10 @@
<dimen name="grip_bar_height">6dp</dimen>
<dimen name="grip_bar_width">120dp</dimen>
<dimen name="corner_radius">@dimen/car_portrait_ui_window_rounded_corner_radius</dimen>
- <dimen name="panel_default_top_margin">517dp</dimen>
+ <dimen name="panel_default_top_margin">612dp</dimen>
<dimen name="panel_drag_threshold">375dp</dimen>
- <dimen name="grip_bar_divider_height">1dp</dimen>
<dimen name="toolbar_back_button_start_margin">24dp</dimen>
+ <dimen name="panel_elevation">36dp</dimen>
+ <dimen name="overlay_icon_size">80dp</dimen>
+ <dimen name="overlay_background_icon_size">120dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/redirections.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/redirections.xml
new file mode 100644
index 0000000..9406a80
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/redirections.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<resources>
+ <!-- Redirections for audio route drawable to make it customizable.-->
+ <drawable name="ic_audio_route_headset">@drawable/ic_smartphone</drawable>
+ <drawable name="ic_audio_route_headset_activatable">@drawable/ic_smartphone_activatable</drawable>
+ <drawable name="ic_audio_route_earpiece">@drawable/ic_smartphone</drawable>
+ <drawable name="ic_audio_route_earpiece_activatable">@drawable/ic_smartphone_activatable</drawable>
+ <drawable name="ic_audio_route_vehicle">@drawable/ic_bluetooth</drawable>
+ <drawable name="ic_audio_route_vehicle_activatable">@drawable/ic_bluetooth_activatable</drawable>
+ <drawable name="ic_audio_route_speaker">@drawable/ic_speaker</drawable>
+ <drawable name="ic_audio_route_speaker_activatable">@drawable/ic_speaker_activatable</drawable>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/strings.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/strings.xml
new file mode 100644
index 0000000..56f3a1e
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2022 Google Inc.
+
+ 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_title">Car launcher reference</string>
+ <string name="base_layer_failure_recovery_title_string">Something went wrong</string>
+ <string name="base_layer_failure_recovery_message_string">Please open a navigation app</string>
+
+ <!-- Audio route selection dialog. Placeholder resources for overriding -->
+ <string name="audio_route_dialog_title">Audio output</string>
+
+ <!-- Audio route -->
+ <!-- Label for routing phone audio to the vehicle using car speakers [CHAR LIMIT=30] -->
+ <string name="audio_route_vehicle">Car speakers</string>
+ <!-- Label for routing phone audio to the phone speaker [CHAR LIMIT=30] -->
+ <string name="audio_route_phone_speaker">Phone speaker</string>
+ <!-- Label for routing phone audio to the phone earpiece. [CHAR LIMIT=30] -->
+ <string name="audio_route_handset">Phone</string>
+</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/styles.xml b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/styles.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values-port/styles.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/values/styles.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/common/UserUnlockReceiver.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/common/UserUnlockReceiver.java
new file mode 100644
index 0000000..221a5dd
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/common/UserUnlockReceiver.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.common;
+
+import static android.content.Intent.ACTION_USER_UNLOCKED;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Broadcast receiver which observes {@link ACTION_USER_UNLOCKED}.
+ */
+public class UserUnlockReceiver extends BroadcastReceiver {
+ private Callback mCallback;
+
+ /**
+ * Registers a broadcastReceiver to listen to {@link ACTION_USER_UNLOCKED} with given
+ * {@code callback} to react on this action.
+ */
+ public void register(Context context, Callback callback) {
+ mCallback = callback;
+ IntentFilter intentFilter = new IntentFilter(ACTION_USER_UNLOCKED);
+ context.registerReceiver(this, intentFilter);
+ }
+
+ /** Unregisters this observer. */
+ public void unregister(Context context) {
+ context.unregisterReceiver(this);
+ mCallback = null;
+ }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mCallback == null || !ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ return;
+ }
+ mCallback.onUserUnlocked();
+ }
+
+ /** Callback interface for {@link UserUnlockReceiver}. */
+ public interface Callback {
+
+ /** Callback triggered when {@link ACTION_USER_UNLOCKED} is received. */
+ void onUserUnlocked();
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardFragment.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardFragment.java
new file mode 100644
index 0000000..746ff34
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardFragment.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.portraitlauncher.controlbar.dialer.DialerCardFragment;
+import com.android.car.portraitlauncher.controlbar.media.MediaCardFragment;
+
+/**
+ * Fragment used to display the audio related controls.
+ */
+public class AudioCardFragment extends Fragment implements HomeCardInterface.View {
+ private View mRootView;
+
+ private MediaCardFragment mMediaFragment;
+ private DialerCardFragment mInCallFragment;
+ private boolean mViewCreated;
+
+ private HomeCardFragment.OnViewLifecycleChangeListener mOnViewLifecycleChangeListener;
+
+ /**
+ * Register a callback to be invoked when the fragment lifecycle changes.
+ *
+ * @param onViewLifecycleChangeListener The callback that will run
+ */
+ public void setOnViewLifecycleChangeListener(
+ HomeCardFragment.OnViewLifecycleChangeListener onViewLifecycleChangeListener) {
+ mOnViewLifecycleChangeListener = onViewLifecycleChangeListener;
+ if (mViewCreated) {
+ mOnViewLifecycleChangeListener.onViewCreated();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mRootView = inflater.inflate(R.layout.car_ui_portrait_audio_card, container, false);
+ return mRootView;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mViewCreated = true;
+ mMediaFragment = new MediaCardFragment();
+ mInCallFragment = new DialerCardFragment();
+
+ FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+ ft.replace(R.id.media_fragment_container, mMediaFragment);
+ ft.replace(R.id.in_call_fragment_container, mInCallFragment);
+ ft.commitNow();
+
+ if (mOnViewLifecycleChangeListener != null) {
+ mOnViewLifecycleChangeListener.onViewCreated();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mViewCreated = false;
+ if (mOnViewLifecycleChangeListener != null) {
+ mOnViewLifecycleChangeListener.onViewDestroyed();
+ }
+ }
+
+ @Override
+ public Fragment getFragment() {
+ return this;
+ }
+
+ @Override
+ public void hideCard() {
+ mRootView.setVisibility(View.GONE);
+ }
+
+ public MediaCardFragment getMediaFragment() {
+ return mMediaFragment;
+ }
+
+ public DialerCardFragment getInCallFragment() {
+ return mInCallFragment;
+ }
+
+ void showMediaCard() {
+ FragmentManager fragmentManager = getChildFragmentManager();
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.show(mMediaFragment);
+ transaction.hide(mInCallFragment);
+ transaction.commit();
+ }
+
+ void showInCallCard() {
+ FragmentManager fragmentManager = getChildFragmentManager();
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.hide(mMediaFragment);
+ transaction.show(mInCallFragment);
+ transaction.commit();
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModel.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModel.java
new file mode 100644
index 0000000..fc93741
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModel.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar;
+
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.InCallModel;
+import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
+import com.android.car.portraitlauncher.controlbar.dialer.DialerCardModel;
+
+/** A wrapper around {@code MediaViewModel} and {@code InCallModel}. */
+public class AudioCardModel implements HomeCardInterface.Model {
+
+ private final MediaViewModel mMediaViewModel;
+ private final InCallModel mInCallViewModel;
+
+ public AudioCardModel(@NonNull ViewModelProvider viewModelProvider) {
+ mMediaViewModel = viewModelProvider.get(MediaViewModel.class);
+ mInCallViewModel = new DialerCardModel(SystemClock.elapsedRealtimeClock());
+ }
+
+ MediaViewModel getMediaViewModel() {
+ return mMediaViewModel;
+ }
+
+ InCallModel getInCallViewModel() {
+ return mInCallViewModel;
+ }
+
+ @Override
+ public void setOnModelUpdateListener(OnModelUpdateListener onModelUpdateListener) {
+ // No-op
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModule.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModule.java
new file mode 100644
index 0000000..e60bbdc
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardModule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar;
+
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.HomeCardModule;
+import com.android.car.portraitlauncher.controlbar.dialer.DialerCardPresenter;
+import com.android.car.portraitlauncher.controlbar.media.MediaCardPresenter;
+
+/**
+ * The Audio card module. This class initializes the necessary components to present an audio card
+ * as a home module.
+ */
+public class AudioCardModule implements HomeCardModule {
+ private AudioCardPresenter mAudioCardPresenter;
+ private AudioCardFragment mAudioCardView;
+ private ViewModelProvider mViewModelProvider;
+ @Override
+ public void setViewModelProvider(ViewModelProvider viewModelProvider) {
+ if (mViewModelProvider != null) {
+ throw new IllegalStateException("Cannot reset the view model provider");
+ }
+ mViewModelProvider = viewModelProvider;
+
+ mAudioCardPresenter = new AudioCardPresenter(
+ new DialerCardPresenter(), new MediaCardPresenter());
+ mAudioCardPresenter.setModel(new AudioCardModel(mViewModelProvider));
+ mAudioCardView = new AudioCardFragment();
+ mAudioCardPresenter.setView(mAudioCardView);
+ }
+
+ @Override
+ public int getCardResId() {
+ return R.id.control_bar;
+ }
+
+ @Override
+ public HomeCardInterface.Presenter getCardPresenter() {
+ return mAudioCardPresenter;
+ }
+
+ @Override
+ public HomeCardInterface.View getCardView() {
+ return mAudioCardView;
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardPresenter.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardPresenter.java
new file mode 100644
index 0000000..534541a
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/AudioCardPresenter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar;
+
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.portraitlauncher.controlbar.dialer.DialerCardPresenter;
+import com.android.car.portraitlauncher.controlbar.media.MediaCardPresenter;
+
+import java.util.List;
+
+/**
+ * Presenter used to coordinate the binding between the audio card model and presentation
+ */
+public class AudioCardPresenter extends CardPresenter {
+
+ // Presenter for the dialer card
+ private final DialerCardPresenter mDialerPresenter;
+
+ // Presenter for the media card
+ private final MediaCardPresenter mMediaPresenter;
+
+ // The fragment controlled by this presenter.
+ private AudioCardFragment mFragment;
+
+ private final HomeCardFragment.OnViewLifecycleChangeListener mOnViewLifecycleChangeListener =
+ new HomeCardFragment.OnViewLifecycleChangeListener() {
+ @Override
+ public void onViewCreated() {
+ mDialerPresenter.setView(mFragment.getInCallFragment());
+ mMediaPresenter.setView(mFragment.getMediaFragment());
+ }
+
+ @Override
+ public void onViewDestroyed() {
+ }
+ };
+
+ public AudioCardPresenter(DialerCardPresenter dialerPresenter,
+ MediaCardPresenter mediaPresenter) {
+ mDialerPresenter = dialerPresenter;
+ mMediaPresenter = mediaPresenter;
+
+ mDialerPresenter.setOnInCallStateChangeListener(hasActiveCall -> {
+ if (hasActiveCall) {
+ mFragment.showInCallCard();
+ } else {
+ mFragment.showMediaCard();
+ }
+ });
+ }
+
+
+ // Deprecated. Use setModel instead.
+ @Override
+ public void setModels(List<HomeCardInterface.Model> models) {
+ // No-op
+ }
+
+ /** Sets the model for this presenter. */
+ public void setModel(AudioCardModel viewModel) {
+ mDialerPresenter.setModel(viewModel.getInCallViewModel());
+ mMediaPresenter.setModel(viewModel.getMediaViewModel());
+ }
+
+ @Override
+ public void setView(HomeCardInterface.View view) {
+ super.setView(view);
+ mFragment = (AudioCardFragment) view;
+ mFragment.setOnViewLifecycleChangeListener(mOnViewLifecycleChangeListener);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardFragment.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardFragment.java
new file mode 100644
index 0000000..2526819
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardFragment.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.portraitlauncher.controlbar.dialer;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Size;
+import android.view.View;
+import android.widget.Chronometer;
+
+import com.android.car.apps.common.BitmapUtils;
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.ui.CardContent;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
+
+/** A fragment for in-call controls. */
+public class DialerCardFragment extends HomeCardFragment {
+ private Chronometer mChronometer;
+ private View mChronometerSeparator;
+
+ private CardContent.CardBackgroundImage mDefaultCardBackgroundImage;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mDefaultCardBackgroundImage = new CardContent.CardBackgroundImage(
+ getContext().getDrawable(R.drawable.default_audio_background),
+ getContext().getDrawable(R.drawable.control_bar_image_background));
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getRootView().setVisibility(View.VISIBLE);
+ getRootView().findViewById(R.id.optional_seek_bar_with_times_container).setVisibility(
+ View.GONE);
+ }
+
+ @Override
+ public void updateContentViewInternal(CardContent content) {
+ if (content.getType() == CardContent.HomeCardContentType.DESCRIPTIVE_TEXT_WITH_CONTROLS) {
+ DescriptiveTextWithControlsView audioContent =
+ (DescriptiveTextWithControlsView) content;
+ updateBackgroundImage(audioContent.getImage());
+ updateDescriptiveTextWithControlsView(audioContent.getTitle(),
+ audioContent.getSubtitle(),
+ /* optionalImage= */ null, audioContent.getLeftControl(),
+ audioContent.getCenterControl(), audioContent.getRightControl());
+ updateAudioDuration(audioContent);
+ } else {
+ super.updateContentViewInternal(content);
+ }
+ }
+
+ @Override
+ protected void hideAllViews() {
+ super.hideAllViews();
+ getCardBackground().setVisibility(View.GONE);
+ }
+
+ private Chronometer getChronometer() {
+ if (mChronometer == null) {
+ mChronometer = getDescriptiveTextWithControlsLayoutView().findViewById(
+ R.id.optional_timer);
+ mChronometerSeparator = getDescriptiveTextWithControlsLayoutView().findViewById(
+ R.id.optional_timer_separator);
+ }
+ return mChronometer;
+ }
+
+ private void updateBackgroundImage(CardContent.CardBackgroundImage cardBackgroundImage) {
+ if (cardBackgroundImage == null || cardBackgroundImage.getForeground() == null) {
+ cardBackgroundImage = mDefaultCardBackgroundImage;
+ }
+ int maxDimen = Math.max(getCardBackgroundImage().getWidth(),
+ getCardBackgroundImage().getHeight());
+ Size scaledSize = new Size(maxDimen, maxDimen);
+ Bitmap imageBitmap = BitmapUtils.fromDrawable(cardBackgroundImage.getForeground(),
+ scaledSize);
+ if (cardBackgroundImage.getBackground() != null) {
+ getCardBackgroundImage().setBackground(cardBackgroundImage.getBackground());
+ getCardBackgroundImage().setClipToOutline(true);
+ }
+ getCardBackgroundImage().setImageBitmap(imageBitmap, /* showAnimation= */ true);
+ getCardBackground().setVisibility(View.VISIBLE);
+ }
+
+ private void updateAudioDuration(DescriptiveTextWithControlsView content) {
+ if (content.getStartTime() > 0) {
+ getChronometer().setVisibility(View.VISIBLE);
+ getChronometer().setBase(content.getStartTime());
+ getChronometer().start();
+ mChronometerSeparator.setVisibility(View.VISIBLE);
+ } else {
+ getChronometer().setVisibility(View.GONE);
+ mChronometerSeparator.setVisibility(View.GONE);
+ }
+ }
+}
+
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardModel.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardModel.java
new file mode 100644
index 0000000..756d9b5
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardModel.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar.dialer;
+
+import android.telecom.Call;
+
+import com.android.car.carlauncher.homescreen.audio.InCallModel;
+import com.android.car.telephony.common.CallDetail;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.time.Clock;
+import java.util.List;
+
+/** A wrapper around InCallModel to track when an active call is in progress. */
+public class DialerCardModel extends InCallModel {
+
+ private boolean mHasActiveCall;
+ private List<Integer> mAvailableRoutes;
+ private int mActiveRoute;
+
+ public DialerCardModel(Clock elapsedTimeClock) {
+ super(elapsedTimeClock);
+ }
+
+ /** Indicates whether there is an active call or not. */
+ public boolean hasActiveCall() {
+ return mHasActiveCall;
+ }
+
+ @Override
+ public void onCallAdded(Call call) {
+ super.onCallAdded(call);
+ mHasActiveCall = call != null;
+ }
+
+ @Override
+ public void onCallRemoved(Call call) {
+ mHasActiveCall = false;
+ super.onCallRemoved(call);
+ }
+
+ @Override
+ protected void handleActiveCall(@NotNull Call call) {
+ CallDetail callDetails = CallDetail.fromTelecomCallDetail(call.getDetails());
+ mAvailableRoutes = sInCallServiceManager.getSupportedAudioRoute(callDetails);
+ mActiveRoute = sInCallServiceManager.getAudioRoute(
+ CallDetail.fromTelecomCallDetail(call.getDetails()).getScoState());
+ super.handleActiveCall(call);
+ }
+
+ /**
+ * Returns audio routes supported by current call.
+ */
+ public List<Integer> getAvailableAudioRoutes() {
+ return mAvailableRoutes;
+ }
+
+ /**
+ * Returns current call audio state.
+ */
+ public int getActiveAudioRoute() {
+ return mActiveRoute;
+ }
+
+ /**
+ * Sets current call audio route.
+ */
+ public void setActiveAudioRoute(int audioRoute) {
+ if (getCurrentCall() == null) {
+ // AudioRouteButton is disabled if it is null. Simply ignore it.
+ return;
+ }
+ sInCallServiceManager.setAudioRoute(audioRoute, getCurrentCall());
+ mActiveRoute = audioRoute;
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardPresenter.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardPresenter.java
new file mode 100644
index 0000000..1c4fd01
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/dialer/DialerCardPresenter.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar.dialer;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.view.Display;
+
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewClickListener;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewLifecycleChangeListener;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.InCallModel;
+
+import java.util.List;
+
+/**
+ * A presenter for the in-call controls.
+ */
+public class DialerCardPresenter extends CardPresenter {
+
+ /** A listener to notify when an in-call state changes. */
+ public interface OnInCallStateChangeListener {
+
+ /** Notifies when an in-call state changes. */
+ void onInCallStateChanged(boolean hasActiveCall);
+ }
+
+ private InCallModel mViewModel;
+ private DialerCardFragment mFragment;
+
+ private boolean mHasActiveCall;
+
+ public void setOnInCallStateChangeListener(
+ OnInCallStateChangeListener onInCallStateChangeListener) {
+ mOnInCallStateChangeListener = onInCallStateChangeListener;
+ }
+
+ private OnInCallStateChangeListener mOnInCallStateChangeListener;
+
+ private final OnViewClickListener mOnViewClickListener =
+ new OnViewClickListener() {
+ @Override
+ public void onViewClicked() {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
+ Intent intent = mViewModel.getIntent();
+ Context context = mFragment.getContext();
+ if (context != null) {
+ context.startActivity(intent, options.toBundle());
+ }
+ }
+ };
+ private final HomeCardInterface.Model.OnModelUpdateListener mOnInCallModelUpdateListener =
+ new HomeCardInterface.Model.OnModelUpdateListener() {
+ @Override
+ public void onModelUpdate(HomeCardInterface.Model model) {
+ DialerCardModel dialerCardModel = (DialerCardModel) model;
+ if (dialerCardModel.getCardHeader() != null) {
+ mFragment.updateHeaderView(dialerCardModel.getCardHeader());
+ }
+ if (dialerCardModel.getCardContent() != null) {
+ mFragment.updateContentView(dialerCardModel.getCardContent());
+ }
+ boolean hasActiveCall = dialerCardModel.hasActiveCall();
+ if (mHasActiveCall != hasActiveCall) {
+ mHasActiveCall = hasActiveCall;
+ mOnInCallStateChangeListener.onInCallStateChanged(hasActiveCall);
+ }
+ }
+ };
+
+ private final OnViewLifecycleChangeListener mOnInCallViewLifecycleChangeListener =
+ new OnViewLifecycleChangeListener() {
+ @Override
+ public void onViewCreated() {
+ mViewModel.setOnModelUpdateListener(mOnInCallModelUpdateListener);
+ mViewModel.onCreate(mFragment.requireContext());
+ }
+
+ @Override
+ public void onViewDestroyed() {
+ mViewModel.onDestroy(getFragment().requireContext());
+ }
+ };
+
+ // Deprecated. Use setModel instead.
+ @Override
+ public void setModels(List<HomeCardInterface.Model> models) {
+ // No-op
+ }
+
+ public void setModel(InCallModel viewModel) {
+ mViewModel = viewModel;
+ }
+
+ @Override
+ public void setView(HomeCardInterface.View view) {
+ super.setView(view);
+ mFragment = (DialerCardFragment) view;
+ mFragment.setOnViewLifecycleChangeListener(mOnInCallViewLifecycleChangeListener);
+ mFragment.setOnViewClickListener(mOnViewClickListener);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardFragment.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardFragment.java
new file mode 100644
index 0000000..357d957
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardFragment.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar.media;
+
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.car.carlauncher.homescreen.audio.AudioFragment;
+
+/** A fragment for the media controls. */
+public class MediaCardFragment extends AudioFragment {
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getRootView().setVisibility(View.VISIBLE);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardPresenter.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardPresenter.java
new file mode 100644
index 0000000..11ffbdb
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaCardPresenter.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.controlbar.media;
+
+import android.content.Intent;
+
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewLifecycleChangeListener;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.AudioFragment;
+import com.android.car.carlauncher.homescreen.audio.AudioModel;
+import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
+
+import java.util.List;
+
+/**
+ * A portrait UI version of {@link MediaCardPresenter}
+ */
+public class MediaCardPresenter extends CardPresenter {
+
+ public final MediaIntentRouter mMediaIntentRouter = MediaIntentRouter.getInstance();
+
+ private MediaViewModel mViewModel;
+ private MediaCardFragment mFragment;
+
+ private final HomeCardFragment.OnViewClickListener mOnViewClickListener =
+ new HomeCardFragment.OnViewClickListener() {
+ @Override
+ public void onViewClicked() {
+ Intent intent = mViewModel.getIntent();
+ mMediaIntentRouter.handleMediaIntent(intent);
+ }
+ };
+
+ private final HomeCardInterface.Model.OnModelUpdateListener mOnMediaModelUpdateListener =
+ new HomeCardInterface.Model.OnModelUpdateListener() {
+ @Override
+ public void onModelUpdate(HomeCardInterface.Model model) {
+ mFragment.updateHeaderView(mViewModel.getCardHeader());
+ mFragment.updateContentView(mViewModel.getCardContent());
+ }
+ };
+
+ private final OnViewLifecycleChangeListener mOnMediaViewLifecycleChangeListener =
+ new OnViewLifecycleChangeListener() {
+ @Override
+ public void onViewCreated() {
+ mViewModel.setOnProgressUpdateListener(mOnMediaProgressUpdateListener);
+ mViewModel.setOnModelUpdateListener(mOnMediaModelUpdateListener);
+ mViewModel.onCreate(getFragment().requireContext());
+ }
+
+ @Override
+ public void onViewDestroyed() {
+ mViewModel.onDestroy(getFragment().requireContext());
+ }
+ };
+
+ private final AudioFragment.OnMediaViewInitializedListener mOnMediaViewInitializedListener =
+ new AudioFragment.OnMediaViewInitializedListener() {
+ @Override
+ public void onMediaViewInitialized() {
+ mFragment.getPlaybackControlsActionBar().setModel(
+ mViewModel.getPlaybackViewModel(),
+ mFragment.getViewLifecycleOwner());
+ }
+ };
+
+ private final AudioModel.OnProgressUpdateListener mOnMediaProgressUpdateListener =
+ new AudioModel.OnProgressUpdateListener() {
+ @Override
+ public void onProgressUpdate(AudioModel model, boolean updateProgress) {
+ if (model == null || model.getCardContent() == null
+ || model.getCardHeader() == null) {
+ return;
+ }
+ DescriptiveTextWithControlsView descriptiveTextWithControlsContent =
+ (DescriptiveTextWithControlsView) model.getCardContent();
+ mFragment.updateProgress(
+ descriptiveTextWithControlsContent.getSeekBarViewModel(),
+ updateProgress);
+ }
+ };
+
+
+ // Deprecated. Use setModel instead.
+ @Override
+ public void setModels(List<HomeCardInterface.Model> models) {
+ // No-op
+ }
+
+ public void setModel(MediaViewModel viewModel) {
+ mViewModel = viewModel;
+ }
+
+ @Override
+ public void setView(HomeCardInterface.View view) {
+ super.setView(view);
+
+ mFragment = (MediaCardFragment) view;
+ mFragment.setOnViewLifecycleChangeListener(mOnMediaViewLifecycleChangeListener);
+ mFragment.setOnViewClickListener(mOnViewClickListener);
+ mFragment.setOnMediaViewInitializedListener(mOnMediaViewInitializedListener);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/MediaIntentRouter.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaIntentRouter.java
similarity index 95%
rename from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/MediaIntentRouter.java
rename to car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaIntentRouter.java
index 12c8ea7..d2e8746 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/MediaIntentRouter.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/controlbar/media/MediaIntentRouter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.portraitlauncher.homescreen.audio;
+package com.android.car.portraitlauncher.controlbar.media;
import android.content.Intent;
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/BackgroundPanelBaseActivity.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/BackgroundPanelBaseActivity.java
new file mode 100644
index 0000000..bbf6a61
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/BackgroundPanelBaseActivity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.homeactivities;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.view.WindowCompat;
+
+import com.android.car.portraitlauncher.R;
+
+/**
+ * Used as the static wallpaper in base layer. This activity is at the bottom of the base layer
+ * stack and is visible when there is no other base layer application is running.
+ */
+public class BackgroundPanelBaseActivity extends AppCompatActivity {
+ private static final String TAG = BackgroundPanelBaseActivity.class.getSimpleName();
+
+ private ViewGroup mContainer;
+
+ private final View.OnApplyWindowInsetsListener mOnApplyWindowInsetsListener = (v, insets) -> {
+ int insetTypes = WindowInsets.Type.systemBars();
+ Insets appliedInsets = insets.getInsets(insetTypes);
+ v.setPadding(appliedInsets.left, /* top= */ 0, appliedInsets.right, /* bottom= */ 0);
+ if (mContainer != null) {
+ mContainer.setPadding(appliedInsets.left, appliedInsets.top, appliedInsets.right,
+ appliedInsets.bottom);
+ } else {
+ Log.e(TAG, "Container is null");
+ }
+ return insets.inset(appliedInsets);
+ };
+
+ /** Creates an intent that can be used to launch this activity. */
+ public static Intent createIntent(Context context) {
+ Intent intent = new Intent(context, BackgroundPanelBaseActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.car_ui_portrait_background_panel_base);
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), /* decorFitsSystemWindows= */ false);
+ mContainer = findViewById(R.id.container);
+ getWindow().getDecorView().getRootView().setOnApplyWindowInsetsListener(
+ mOnApplyWindowInsetsListener);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/CarUiPortraitHomeScreen.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/CarUiPortraitHomeScreen.java
index ad46f00..8149833 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/CarUiPortraitHomeScreen.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/CarUiPortraitHomeScreen.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.MSG_APP_GRID_VISIBILITY_CHANGE;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.MSG_COLLAPSE_NOTIFICATION;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.MSG_COLLAPSE_RECENTS;
@@ -39,6 +40,7 @@
import android.app.IActivityManager;
import android.app.TaskInfo;
import android.app.TaskStackListener;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -48,6 +50,7 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
import android.os.Build;
@@ -67,6 +70,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.core.view.WindowCompat;
@@ -77,8 +81,6 @@
import com.android.car.carlauncher.CarLauncher;
import com.android.car.carlauncher.CarLauncherUtils;
import com.android.car.carlauncher.CarTaskView;
-import com.android.car.carlauncher.ControlledCarTaskViewCallbacks;
-import com.android.car.carlauncher.ControlledCarTaskViewConfig;
import com.android.car.carlauncher.LaunchRootCarTaskViewCallbacks;
import com.android.car.carlauncher.SemiControlledCarTaskViewCallbacks;
import com.android.car.carlauncher.TaskViewManager;
@@ -87,7 +89,8 @@
import com.android.car.caruiportrait.common.service.CarUiPortraitService;
import com.android.car.portraitlauncher.R;
import com.android.car.portraitlauncher.common.IntentHandler;
-import com.android.car.portraitlauncher.homescreen.audio.MediaIntentRouter;
+import com.android.car.portraitlauncher.common.UserUnlockReceiver;
+import com.android.car.portraitlauncher.controlbar.media.MediaIntentRouter;
import com.android.car.portraitlauncher.panel.TaskViewPanel;
import java.lang.reflect.InvocationTargetException;
@@ -146,6 +149,7 @@
public final class CarUiPortraitHomeScreen extends FragmentActivity {
public static final String TAG = CarUiPortraitHomeScreen.class.getSimpleName();
private static final boolean DBG = Build.IS_DEBUGGABLE;
+ private static final long IMMERSIVE_MODE_REQUEST_TIMEOUT = 500;
private static final String SAVED_BACKGROUND_APP_COMPONENT_NAME =
"SAVED_BACKGROUND_APP_COMPONENT_NAME";
private static final IActivityManager sActivityManager = ActivityManager.getService();
@@ -153,7 +157,7 @@
private int mCurrentBackgroundTaskId;
private int mStatusBarHeight;
private FrameLayout mContainer;
- private View mControlBarView;
+ private LinearLayout mControlBarView;
private TaskViewManager mTaskViewManager;
// All the TaskViews & corresponding helper instance variables.
private CarTaskView mBackgroundTaskView;
@@ -163,14 +167,18 @@
private int mNavBarHeight;
private boolean mIsSUWInProgress;
private TaskCategoryManager mTaskCategoryManager;
+ private TaskInfo mCurrentTaskInRootTaskView;
private boolean mIsNotificationCenterOnTop;
private boolean mIsRecentsOnTop;
private TaskInfoCache mTaskInfoCache;
private TaskViewPanel mAppGridTaskViewPanel;
private TaskViewPanel mRootTaskViewPanel;
-
private InputManagerGlobal mInputManagerGlobal;
+ private ComponentName mUnhandledImmersiveModeRequestComponent;
+ private long mUnhandledImmersiveModeRequestTimestamp;
+ private boolean mUnhandledImmersiveModeRequest;
+
/** Messenger for communicating with {@link CarUiPortraitService}. */
private Messenger mService = null;
/** Flag indicating whether or not {@link CarUiPortraitService} is bounded. */
@@ -185,11 +193,12 @@
private final List<Message> mMessageCache = new ArrayList<>();
private CarUiPortraitDriveStateController mCarUiPortraitDriveStateController;
+ private final UserUnlockReceiver mUserUnlockReceiver = new UserUnlockReceiver();
private final IntentHandler mMediaIntentHandler = new IntentHandler() {
@Override
public void handleIntent(Intent intent) {
- if (TaskCategoryManager.isMediaApp(mRootTaskViewPanel.getCurrentTask())) {
+ if (TaskCategoryManager.isMediaApp(mTaskViewManager.getTopTaskInLaunchRootTask())) {
mRootTaskViewPanel.closePanel();
return;
}
@@ -243,18 +252,6 @@
logIfDebuggable("On task created, task = " + taskId
+ " componentName " + componentName);
}
-
- // Re-launch the CarUiPortraitHomeScreen if coming background app is not current
- // background app.
- if (mTaskCategoryManager.isBackgroundApp(componentName)) {
- mCurrentBackgroundTaskId = taskId;
- if (!mTaskCategoryManager.isCurrentBackgroundApp(componentName)) {
- logIfDebuggable(
- "Re-create home screen on background app switch to " + componentName);
- mTaskCategoryManager.setCurrentBackgroundApp(componentName);
- recreate();
- }
- }
}
@Override
@@ -282,25 +279,28 @@
return;
}
+ adjustFullscreenSpacing(mTaskCategoryManager.isFullScreenActivity(taskInfo));
+
mIsNotificationCenterOnTop = mTaskCategoryManager.isNotificationActivity(taskInfo);
mIsRecentsOnTop = mTaskCategoryManager.isRecentsActivity(taskInfo);
- // Close the panel if the top application is a blank activity.
- // This is to prevent showing a blank panel to the user if an app crashes and reveals
- // the blank activity underneath.
- if (mTaskCategoryManager.isBlankActivity(taskInfo)) {
- setFocusToBackgroundApp();
- return;
- }
if (mTaskCategoryManager.isBackgroundApp(taskInfo)) {
+ mTaskCategoryManager.setCurrentBackgroundApp(taskInfo.baseActivity);
return;
}
if (shouldTaskShowOnRootTaskView(taskInfo)) {
- logIfDebuggable("Opening in root task view: ");
- mRootTaskViewPanel.setCurrentTask(taskInfo);
-
- if (mAppGridTaskViewPanel.isOpen()) {
+ logIfDebuggable("Opening in root task view: " + taskInfo);
+ mRootTaskViewPanel.setComponentName(getVisibleActivity(taskInfo));
+ mCurrentTaskInRootTaskView = taskInfo;
+ // Open immersive mode if there is unhandled immersive mode request.
+ if (shouldOpenFullScreenPanel(taskInfo)) {
+ mRootTaskViewPanel.openFullScreenPanel(/* animated= */ true,
+ /* showToolBar= */ true, mNavBarHeight);
+ resetObscuredTouchRegion();
+ setUnhandledImmersiveModeRequest(/* componentName= */ null, /* timestamp= */ 0,
+ /* requested= */ false);
+ } else if (mAppGridTaskViewPanel.isOpen()) {
// Animate the root task view to expand on top of the app grid task view.
mRootTaskViewPanel.expandPanel();
} else {
@@ -313,6 +313,23 @@
}
/**
+ * Called when a task is removed.
+ * @param taskId id of the task.
+ * @throws RemoteException
+ */
+ @Override
+ public void onTaskRemoved(int taskId) throws RemoteException {
+ super.onTaskRemoved(taskId);
+
+ // Hide the root task view panel if it is empty.
+ if (mRootTaskViewPanel != null
+ && mTaskViewManager.getRootTaskCount() == 0
+ && mRootTaskViewPanel.isOpen()) {
+ mRootTaskViewPanel.closePanel(false);
+ }
+ }
+
+ /**
* Called whenever IActivityManager.startActivity is called on an activity that is already
* running, but the task is either brought to the front or a new Intent is delivered to it.
*
@@ -338,8 +355,9 @@
return;
}
- if (mTaskCategoryManager.isBackgroundApp(taskInfo)
- || mTaskCategoryManager.isBlankActivity(taskInfo)) {
+ adjustFullscreenSpacing(mTaskCategoryManager.isFullScreenActivity(taskInfo));
+
+ if (mTaskCategoryManager.isBackgroundApp(taskInfo)) {
return;
}
@@ -360,10 +378,14 @@
} else if (mRootTaskViewPanel.isOpen()) {
mAppGridTaskViewPanel.fadeInPanel();
mRootTaskViewPanel.fadeOutPanel();
+ } else if (mRootTaskViewPanel.isFullScreen()) {
+ mAppGridTaskViewPanel.openPanel();
+ mRootTaskViewPanel.closePanel();
} else {
mAppGridTaskViewPanel.openPanel();
}
} else if (shouldTaskShowOnRootTaskView(taskInfo)) {
+ mRootTaskViewPanel.setComponentName(getVisibleActivity(taskInfo));
if (mAppGridTaskViewPanel.isAnimating() && mAppGridTaskViewPanel.isOpen()) {
mAppGridTaskViewPanel.openPanel(/* animated = */ false);
}
@@ -373,9 +395,11 @@
// center. Make sure the app grid panel is closed already in case we are
// interrupting a running animation.
// 2 - Open the root task view panel if it is closed:
- // a) If the app grid panel is already open then use an expand animation
+ // a) If there is a fresh unhandled immersive mode request, open the root task
+ // view panel to full screen.
+ // b) If the app grid panel is already open then use an expand animation
// to open the root task view on top of the app grid task view.
- // b) Otherwise, simply open the app grid panel.
+ // c) Otherwise, simply open the root task view panel.
if (mRootTaskViewPanel.isOpen()
&& mTaskCategoryManager.isNotificationActivity(taskInfo)) {
if (mAppGridTaskViewPanel.isOpen()) {
@@ -383,7 +407,13 @@
}
mRootTaskViewPanel.closePanel();
} else {
- if (mAppGridTaskViewPanel.isOpen()) {
+ if (shouldOpenFullScreenPanel(taskInfo)) {
+ mRootTaskViewPanel.openFullScreenPanel(/* animated= */ true,
+ /* showToolBar= */ true, mNavBarHeight);
+ resetObscuredTouchRegion();
+ setUnhandledImmersiveModeRequest(/* componentName= */ null,
+ /* timestamp= */ 0, /* requested= */ false);
+ } else if (mAppGridTaskViewPanel.isOpen()) {
mRootTaskViewPanel.expandPanel();
} else {
mRootTaskViewPanel.openPanel();
@@ -401,6 +431,13 @@
}
}
+ private boolean shouldOpenFullScreenPanel(ActivityManager.RunningTaskInfo taskInfo) {
+ return taskInfo.baseActivity.equals(mUnhandledImmersiveModeRequestComponent)
+ && mUnhandledImmersiveModeRequest
+ && System.currentTimeMillis() - mUnhandledImmersiveModeRequestTimestamp
+ < IMMERSIVE_MODE_REQUEST_TIMEOUT;
+ }
+
/**
* Only resize the size of rootTaskView when SUW is in progress. This is to resize the height of
* rootTaskView after status bar hide on SUW start.
@@ -416,6 +453,7 @@
if (mIsSUWInProgress) {
mRootTaskViewPanel.openFullScreenPanel(/* animated = */ false,
/* showToolBar = */ false, /* bottomAdjustment= */ 0);
+ resetObscuredTouchRegion();
}
};
@@ -452,6 +490,8 @@
setContentView(R.layout.car_ui_portrait_launcher);
+ registerUserUnlockReceiver();
+
mTaskCategoryManager = new TaskCategoryManager(getApplicationContext());
if (savedInstanceState != null) {
String savedBackgroundAppName = savedInstanceState.getString(
@@ -472,8 +512,8 @@
com.android.internal.R.dimen.navigation_bar_height);
logIfDebuggable("Navbar height: " + mNavBarHeight);
mContainer = findViewById(R.id.container);
- mContainer.addOnLayoutChangeListener(mHomeScreenLayoutChangeListener);
setHomeScreenBottomPadding(mNavBarHeight);
+ mContainer.addOnLayoutChangeListener(mHomeScreenLayoutChangeListener);
mAppGridTaskViewPanel = findViewById(R.id.app_grid_panel);
@@ -539,6 +579,14 @@
mAppGridTaskViewPanel.closePanel();
}
+ private void registerUserUnlockReceiver() {
+ UserUnlockReceiver.Callback callback = () -> {
+ logIfDebuggable("On user unlock");
+ initSemiControlledTaskViews();
+ };
+ mUserUnlockReceiver.register(this, callback);
+ }
+
private void initializeCards() {
Set<HomeCardModule> homeCardModules = new androidx.collection.ArraySet<>();
for (String providerClassName : getResources().getStringArray(
@@ -563,7 +611,7 @@
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
for (HomeCardModule cardModule : homeCardModules) {
- transaction.replace(cardModule.getCardResId(), cardModule.getCardView());
+ transaction.replace(cardModule.getCardResId(), cardModule.getCardView().getFragment());
}
transaction.commitNow();
}
@@ -585,8 +633,13 @@
super.onConfigurationChanged(newConfig);
initializeCards();
+ Drawable background =
+ getResources().getDrawable(R.drawable.control_bar_background, getTheme());
+ mControlBarView.setBackground(background);
+
mRootTaskViewPanel.post(() -> mRootTaskViewPanel.refresh(getTheme()));
mAppGridTaskViewPanel.post(() -> mAppGridTaskViewPanel.refresh(getTheme()));
+ updateBackgroundTaskViewInsets();
}
@Override
@@ -605,6 +658,8 @@
mRootTaskViewPanel.onDestroy();
mBackgroundTaskView = null;
mFullScreenTaskView = null;
+ mTaskCategoryManager.onDestroy();
+ mUserUnlockReceiver.unregister(this);
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
doUnbindService();
super.onDestroy();
@@ -654,11 +709,40 @@
}
}
+ private void setBackgroundTaskViewBottomMargin(int bottomMargin) {
+ if (mBackgroundTaskView == null) {
+ return;
+ }
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mBackgroundTaskView.getLayoutParams();
+ params.setMargins(/* left= */ 0, /* top= */ 0, /* right= */ 0, bottomMargin);
+ mBackgroundTaskView.requestLayout();
+ }
+
+ private void setControlBarSpacerVisibility(boolean isVisible) {
+ if (mControlBarView == null) {
+ return;
+ }
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mControlBarView.getLayoutParams();
+ params.setMargins(/* left= */ 0, /* top= */ 0, /* right= */ 0,
+ isVisible ? mNavBarHeight : 0);
+ mControlBarView.requestLayout();
+ }
+
private void setHomeScreenBottomPadding(int bottomPadding) {
// Set padding instead of margin so the bottom area shows background of
// car_ui_portrait_launcher during immersive mode without nav bar, and panel states are
// calculated correctly.
- mContainer.setPadding(/* left= */ 0, /* top= */ 0, /* right= */0, bottomPadding);
+ mContainer.setPadding(/* left= */ 0, /* top= */ 0, /* right= */ 0, bottomPadding);
+ }
+
+ private void adjustFullscreenSpacing(boolean isFullscreen) {
+ logIfDebuggable(isFullscreen
+ ? "Adjusting screen spacing for fullscreen task view"
+ : "Adjusting screen spacing for non-fullscreen task views");
+ setControlBarSpacerVisibility(isFullscreen);
+ setHomeScreenBottomPadding(isFullscreen ? 0 : mNavBarHeight);
}
// TODO(b/275633095): Add test to verify the region is set correctly in each mode
@@ -694,14 +778,27 @@
mFullScreenTaskView.setObscuredTouchRegion(obscuredTouchRegion);
}
+ private void resetObscuredTouchRegion() {
+ Rect resetRegion = new Rect(0, 0, 0, 0);
+ Region resetTouchRegion = new Region();
+ resetTouchRegion.union(resetRegion);
+ mRootTaskViewPanel.setObscuredTouchRegion(resetTouchRegion);
+ mAppGridTaskViewPanel.setObscuredTouchRegion(resetTouchRegion);
+ mBackgroundTaskView.setObscuredTouchRegion(resetTouchRegion);
+ mFullScreenTaskView.setObscuredTouchRegion(resetTouchRegion);
+ }
+
private void updateBackgroundTaskViewInsets() {
if (mBackgroundTaskView == null) {
return;
}
- int bottomOverlap = Math.min(mControlBarView.getTop(),
- mRootTaskViewPanel.getTop());
- bottomOverlap = Math.min(bottomOverlap, mAppGridTaskViewPanel.getTop());
+ int bottomOverlap = mControlBarView.getTop();
+ if (mRootTaskViewPanel.isVisible()) {
+ bottomOverlap = mRootTaskViewPanel.getTop();
+ } else if (mAppGridTaskViewPanel.isVisible()) {
+ bottomOverlap = mAppGridTaskViewPanel.getTop();
+ }
Rect appAreaBounds = new Rect();
mBackgroundTaskView.getBoundsOnScreen(appAreaBounds);
@@ -727,7 +824,7 @@
Rect appAreaBounds = new Rect();
mFullScreenTaskView.getBoundsOnScreen(appAreaBounds);
- Rect bottomInsets = new Rect(appAreaBounds.left, appAreaBounds.height(),
+ Rect bottomInsets = new Rect(appAreaBounds.left, appAreaBounds.height() - mNavBarHeight,
appAreaBounds.right, appAreaBounds.bottom);
Rect topInsets = new Rect(appAreaBounds.left, appAreaBounds.top, appAreaBounds.right,
@@ -772,40 +869,84 @@
private void setUpBackgroundTaskView() {
ViewGroup parent = findViewById(R.id.background_app_area);
- Intent backgroundIntent = mTaskCategoryManager.getCurrentBackgroundApp() == null
- ? CarLauncherUtils.getMapsIntent(getApplicationContext())
- : (new Intent()).setComponent(mTaskCategoryManager.getCurrentBackgroundApp());
-
- mTaskViewManager.createControlledCarTaskView(getMainExecutor(),
- ControlledCarTaskViewConfig.builder()
- .setActivityIntent(backgroundIntent)
- .setAutoRestartOnCrash(/* autoRestartOnCrash- */ true)
- .build(),
- new ControlledCarTaskViewCallbacks() {
+ mTaskViewManager.createSemiControlledTaskView(getMainExecutor(),
+ mTaskCategoryManager.getBackgroundActivities().stream().toList(),
+ new SemiControlledCarTaskViewCallbacks() {
@Override
public void onTaskViewCreated(CarTaskView taskView) {
- logIfDebuggable("Background Task View is created with component = "
- + backgroundIntent.getComponent());
+ logIfDebuggable("Background Task View is created");
taskView.setZOrderOnTop(false);
mBackgroundTaskView = taskView;
parent.addView(mBackgroundTaskView);
- // Set the background app here to avoid recreating
- // CarUiPortraitHomeScreen in onTaskCreated
- mTaskCategoryManager.setCurrentBackgroundApp(
- backgroundIntent.getComponent());
}
@Override
public void onTaskViewReady() {
logIfDebuggable("Background Task View is ready");
mIsBackgroundTaskViewReady = true;
+ startBackgroundActivities();
onTaskViewReadinessUpdated();
updateBackgroundTaskViewInsets();
+ registerOnBackgroundApplicationInstallUninstallListener();
}
}
);
}
+ private void startBackgroundActivities() {
+ logIfDebuggable("start background activities");
+ Intent backgroundIntent = mTaskCategoryManager.getCurrentBackgroundApp() == null
+ ? CarLauncherUtils.getMapsIntent(getApplicationContext())
+ : (new Intent()).setComponent(mTaskCategoryManager.getCurrentBackgroundApp());
+
+ Intent failureRecoveryIntent =
+ BackgroundPanelBaseActivity.createIntent(getApplicationContext());
+
+ Intent[] intents = {failureRecoveryIntent, backgroundIntent};
+
+ startActivitiesInternal(intents);
+
+ // Set the background app here to avoid recreating
+ // CarUiPortraitHomeScreen in onTaskCreated
+ mTaskCategoryManager.setCurrentBackgroundApp(backgroundIntent.getComponent());
+ }
+
+
+ /** Starts given {@code intents} in order. */
+ private void startActivitiesInternal(Intent[] intents) {
+ for (Intent intent : intents) {
+ startActivityInternal(intent);
+ }
+ }
+
+ /** Starts given {@code intent}. */
+ private void startActivityInternal(Intent intent) {
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to launch", e);
+ }
+ }
+
+ private void registerOnBackgroundApplicationInstallUninstallListener() {
+ mTaskCategoryManager.registerOnApplicationInstallUninstallListener(
+ new TaskCategoryManager.OnApplicationInstallUninstallListener() {
+ @Override
+ public void onAppInstalled(String packageName) {
+ mTaskViewManager.setAllowListedActivities(
+ mBackgroundTaskView,
+ mTaskCategoryManager.getBackgroundActivities().stream().toList());
+ }
+
+ @Override
+ public void onAppUninstall(String packageName) {
+ mTaskViewManager.setAllowListedActivities(
+ mBackgroundTaskView,
+ mTaskCategoryManager.getBackgroundActivities().stream().toList());
+ }
+ });
+ };
+
private void setControlBarVisibility(boolean isVisible, boolean animate) {
float translationY = isVisible ? 0 : mContainer.getHeight() - mControlBarView.getTop();
if (animate) {
@@ -816,17 +957,13 @@
mControlBarView.setTranslationY(translationY);
updateObscuredTouchRegion();
}
-
}
private void setUpAppGridTaskView() {
mAppGridTaskViewPanel.setTag("AppGridPanel");
- mTaskViewManager.createControlledCarTaskView(getMainExecutor(),
- ControlledCarTaskViewConfig.builder()
- .setActivityIntent(CarLauncherUtils.getAppsGridIntent())
- .setAutoRestartOnCrash(/* autoRestartOnCrash= */ true)
- .build(),
- new ControlledCarTaskViewCallbacks() {
+ mTaskViewManager.createSemiControlledTaskView(getMainExecutor(),
+ List.of(mTaskCategoryManager.getAppGridActivity()),
+ new SemiControlledCarTaskViewCallbacks() {
@Override
public void onTaskViewCreated(CarTaskView taskView) {
taskView.setZOrderOnTop(false);
@@ -838,6 +975,7 @@
logIfDebuggable("App grid Task View is ready");
mAppGridTaskViewPanel.setReady(true);
onTaskViewReadinessUpdated();
+ startActivity(CarLauncherUtils.getAppsGridIntent());
}
}
);
@@ -867,6 +1005,33 @@
&& mIsBackgroundTaskViewReady && mIsFullScreenTaskViewReady;
}
+
+ /**
+ * Initialize {@link SemiControlledTaskView}s. Proceed after both {@link TaskCategoryManager}
+ * and {@code mRootTaskViewPanel} are ready.
+ *
+ * <p>Note: 1. After flashing device and FRX, {@link UserUnlockReceiver} doesn't receive
+ * {@link android.content.Intent.ACTION_USER_UNLOCKED}, but PackageManager already starts
+ * resolving intent right after {@link mRootTaskViewPanel} is ready. So initialize
+ * {@link SemiControlledTaskView}s directly. 2. For device boot later, PackageManager starts to
+ * resolving intent after {@link android.content.Intent.ACTION_USER_UNLOCKED}, so wait
+ * until {@link UserUnlockReceiver} notify {@link CarUiPortraitHomeScreen}.
+ */
+ private void initSemiControlledTaskViews() {
+ if (!mTaskCategoryManager.isReady() || !mRootTaskViewPanel.isReady()
+ || isAllTaskViewsReady()) {
+ return;
+ }
+
+ // BackgroundTaskView and FullScreenTaskView are init with activities lists provided by
+ // mTaskCategoryManager. mTaskCategoryManager needs refresh to get up-to-date activities
+ // lists.
+ mTaskCategoryManager.refresh();
+ setUpBackgroundTaskView();
+ setUpAppGridTaskView();
+ setUpFullScreenTaskView();
+ }
+
private void onTaskViewReadinessUpdated() {
if (!isAllTaskViewsReady()) {
return;
@@ -882,6 +1047,8 @@
+ "( visible: " + isControlBarVisible
+ ", bounds:" + controlBarBounds
+ ")");
+
+ mTaskInfoCache.startCachedTasks();
}
private void setUpRootTaskView() {
@@ -893,7 +1060,6 @@
TaskViewPanel.State newState, boolean animated) {
boolean isFullScreen = newState.isFullScreen();
if (isFullScreen) {
- setHomeScreenBottomPadding(mIsSUWInProgress ? 0 : mNavBarHeight);
if (!mIsSUWInProgress) {
notifySystemUI(MSG_HIDE_SYSTEM_BAR_FOR_IMMERSIVE, boolToInt(isFullScreen));
}
@@ -908,6 +1074,10 @@
notifySystemUI(MSG_APP_GRID_VISIBILITY_CHANGE, boolToInt(false));
}
+ if (mCurrentTaskInRootTaskView != null && isVisible) {
+ mTaskViewManager.updateTaskVisibility(mCurrentTaskInRootTaskView.token, true);
+ }
+
// Update the notification button's selection state.
if (mIsNotificationCenterOnTop && isVisible) {
notifySystemUI(MSG_NOTIFICATIONS_VISIBILITY_CHANGE, boolToInt(true));
@@ -927,13 +1097,10 @@
public void onStateChangeEnd(TaskViewPanel.State oldState,
TaskViewPanel.State newState, boolean animated) {
updateObscuredTouchRegion();
-
// Hide the control bar after the animation if in full screen.
if (newState.isFullScreen()) {
setControlBarVisibility(/* isVisible= */ false, animated);
} else {
- // Adjust the bottom margin to count for the nav bar.
- setHomeScreenBottomPadding(mNavBarHeight);
// Update the background task view insets to make sure their content is not
// covered with our panels. We only need to do this when we are not in
// fullscreen.
@@ -948,9 +1115,10 @@
// Hide the app grid task view behind the root task view.
if (newState.isVisible()) {
mAppGridTaskViewPanel.closePanel(/* animated = */ false);
- } else {
- // Launch a blank activity to move the top activity to background.
- startActivity(BlankActivity.createIntent(getApplicationContext()));
+ } else if (mCurrentTaskInRootTaskView != null && oldState.isVisible()) {
+ // hide the window of the task running in the root task view.
+ logIfDebuggable("hiding the window for task: " + mCurrentTaskInRootTaskView);
+ mTaskViewManager.updateTaskVisibility(mCurrentTaskInRootTaskView.token, false);
}
}
});
@@ -969,12 +1137,8 @@
public void onTaskViewReady() {
logIfDebuggable("Root Task View is ready");
mRootTaskViewPanel.setReady(true);
- mTaskInfoCache.startCachedTasks();
onTaskViewReadinessUpdated();
-
- setUpBackgroundTaskView();
- setUpAppGridTaskView();
- setUpFullScreenTaskView();
+ initSemiControlledTaskViews();
}
});
}
@@ -1021,20 +1185,70 @@
});
}
- private void onImmersiveModeRequested(boolean requested, boolean animate) {
- logIfDebuggable("onImmersiveModeRequested = " + requested);
- if (requested && (!mCarUiPortraitDriveStateController.isDrivingStateMoving()
- || mIsSUWInProgress)) {
- int bottomAdjustment = mIsSUWInProgress ? 0 : mNavBarHeight;
- mRootTaskViewPanel.openFullScreenPanel(animate, !mIsSUWInProgress, bottomAdjustment);
- } else {
- if (mTaskViewManager.getRootTaskCount() > 0) {
- mRootTaskViewPanel.openPanel(animate);
- } else {
- // Don't animate if there is no task in the panel.
- mRootTaskViewPanel.closePanel(/* animated = */ false);
- }
+ private void onImmersiveModeRequested(boolean requested, ComponentName componentName) {
+ logIfDebuggable("onImmersiveModeRequested = " + requested + " cmp=" + componentName);
+
+ // Ignore the immersive mode request for app grid, since it's not in root task view panel.
+ // Handle the app grid task in TaskStackListener.
+ if (mTaskCategoryManager.isAppGridActivity(componentName)) {
+ return;
}
+
+ if (componentName == null) {
+ // Quit immersive mode if the car is moving and in immersive mode.
+ if (mCarUiPortraitDriveStateController.isDrivingStateMoving()
+ && mRootTaskViewPanel.isFullScreen()) {
+ mRootTaskViewPanel.openPanel();
+ }
+ return;
+ }
+
+ // Only handles the immersive mode request here if requesting component has the same package
+ // name as the current top task.
+ if (!isPackageVisibleOnRootTask(componentName)) {
+ // Save the component and timestamp of the latest immersive mode request, in case any
+ // race condition with TaskStackListener.
+ setUnhandledImmersiveModeRequest(componentName, System.currentTimeMillis(), requested);
+ return;
+ }
+
+ if (requested) {
+ mRootTaskViewPanel.openFullScreenPanel(/* animated= */ true, /* showToolBar= */ true,
+ mNavBarHeight);
+ resetObscuredTouchRegion();
+ } else {
+ mRootTaskViewPanel.openPanelWithIcon();
+ }
+ }
+
+ private boolean isPackageVisibleOnRootTask(ComponentName componentName) {
+ ActivityManager.RunningTaskInfo taskInfo = mTaskViewManager.getTopTaskInLaunchRootTask();
+ logIfDebuggable("Top task in launch root task is" + taskInfo);
+ if (taskInfo == null) {
+ return false;
+ }
+
+ ComponentName visibleComponentName = getVisibleActivity(taskInfo);
+
+ return visibleComponentName != null
+ && componentName.getPackageName().equals(visibleComponentName.getPackageName());
+ }
+
+ private ComponentName getVisibleActivity(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.topActivity != null) {
+ return taskInfo.topActivity;
+ } else if (taskInfo.baseActivity != null) {
+ return taskInfo.baseActivity;
+ } else {
+ return taskInfo.baseIntent.getComponent();
+ }
+ }
+
+ private void setUnhandledImmersiveModeRequest(ComponentName componentName, long timestamp,
+ boolean requested) {
+ mUnhandledImmersiveModeRequestComponent = componentName;
+ mUnhandledImmersiveModeRequestTimestamp = timestamp;
+ mUnhandledImmersiveModeRequest = requested;
}
/**
@@ -1050,12 +1264,19 @@
recreate();
break;
case MSG_IMMERSIVE_MODE_REQUESTED:
- onImmersiveModeRequested(intToBool(msg.arg1), /* animate = */ true);
+ onImmersiveModeRequested(intToBool(msg.arg1),
+ getComponentNameFromBundle(msg.getData()));
break;
case MSG_SUW_IN_PROGRESS:
mIsSUWInProgress = intToBool(msg.arg1);
logIfDebuggable("Get intent about the SUW is " + mIsSUWInProgress);
- onImmersiveModeRequested(mIsSUWInProgress, /* animate = */ false);
+ if (mIsSUWInProgress) {
+ mRootTaskViewPanel.openFullScreenPanel(/* animated= */false,
+ /* showToolBar= */ false, /* bottomAdjustment= */ 0);
+ resetObscuredTouchRegion();
+ } else {
+ mRootTaskViewPanel.closePanel();
+ }
break;
case MSG_IMMERSIVE_MODE_CHANGE:
boolean hideNavBar = intToBool(msg.arg1);
@@ -1073,6 +1294,13 @@
}
}
+ private ComponentName getComponentNameFromBundle(Bundle bundle) {
+ String cmpString = bundle.getString(INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE);
+ return (cmpString == null)
+ ? null
+ : ComponentName.unflattenFromString(cmpString);
+ }
+
void doBindService() {
// Establish a connection with {@link CarUiPortraitService}. We use an explicit class
// name because there is no reason to be able to let other applications replace our
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/TaskCategoryManager.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/TaskCategoryManager.java
index 9e7be5c..c0eb6f0 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/TaskCategoryManager.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homeactivities/TaskCategoryManager.java
@@ -16,22 +16,31 @@
package com.android.car.portraitlauncher.homeactivities;
+import android.annotation.MainThread;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.car.media.CarMediaIntents;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Build;
-import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import androidx.car.app.CarContext;
+
import com.android.car.carlauncher.AppGridActivity;
+import com.android.car.carlauncher.CarLauncherUtils;
import com.android.car.portraitlauncher.R;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -42,100 +51,49 @@
class TaskCategoryManager {
public static final String TAG = TaskCategoryManager.class.getSimpleName();
private static final boolean DBG = Build.IS_DEBUGGABLE;
- private final ComponentName mBlankActivityComponent;
+ /** Stub geo data to help query navigation intent. */
+ private static final String STUB_GEO_DATA = "geo:0.0,0,0";
+
private final ComponentName mAppGridActivityComponent;
private final ComponentName mNotificationActivityComponent;
private final ComponentName mRecentsActivityComponent;
private final ArraySet<ComponentName> mIgnoreOpeningRootTaskViewComponentsSet;
private final Set<ComponentName> mFullScreenActivities;
private final Set<ComponentName> mBackgroundActivities;
-
- private ComponentName mCurrentBackgroundApp;
-
private final Context mContext;
+ private final ApplicationInstallUninstallReceiver mApplicationInstallUninstallReceiver;
+ private final Set<OnApplicationInstallUninstallListener>
+ mOnApplicationInstallUninstallListeners;
+ private ComponentName mCurrentBackgroundApp;
TaskCategoryManager(Context context) {
mContext = context;
- mFullScreenActivities = convertToComponentNames(mContext.getResources()
- .getStringArray(R.array.config_fullScreenActivities));
- mBackgroundActivities = convertToComponentNames(mContext.getResources()
- .getStringArray(R.array.config_backgroundActivities));
+ mFullScreenActivities = new HashSet<>();
+ mBackgroundActivities = new HashSet<>();
mIgnoreOpeningRootTaskViewComponentsSet = convertToComponentNames(mContext.getResources()
.getStringArray(R.array.config_ignoreOpeningForegroundDA));
mAppGridActivityComponent = new ComponentName(context, AppGridActivity.class);
- mBlankActivityComponent = new ComponentName(context, BlankActivity.class);
mNotificationActivityComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(R.string.config_notificationActivity));
mRecentsActivityComponent = ComponentName.unflattenFromString(mContext.getResources()
.getString(com.android.internal.R.string.config_recentsComponentName));
+ mOnApplicationInstallUninstallListeners = new HashSet<>();
+
updateVoicePlateActivityMap();
+ updateBackgroundActivityMap();
+
+ mApplicationInstallUninstallReceiver = registerApplicationInstallUninstallReceiver(
+ mContext);
}
- void updateVoicePlateActivityMap() {
- Context currentUserContext = mContext.createContextAsUser(
- UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0);
-
- Intent voiceIntent = new Intent(Intent.ACTION_VOICE_ASSIST, /* uri= */ null);
- List<ResolveInfo> result = currentUserContext.getPackageManager().queryIntentActivities(
- voiceIntent, PackageManager.MATCH_ALL);
-
- for (ResolveInfo info : result) {
- if (mFullScreenActivities.add(info.activityInfo.getComponentName())) {
- logIfDebuggable("adding the following component to show on fullscreen: "
- + info.activityInfo.getComponentName());
- }
- }
- }
-
- boolean isBackgroundApp(TaskInfo taskInfo) {
- return mBackgroundActivities.contains(taskInfo.baseActivity);
- }
-
- boolean isBackgroundApp(ComponentName componentName) {
- return mBackgroundActivities.contains(componentName);
- }
-
- boolean isCurrentBackgroundApp(ComponentName componentName) {
- return mCurrentBackgroundApp != null && mCurrentBackgroundApp.equals(componentName);
- }
-
- ComponentName getCurrentBackgroundApp() {
- return mCurrentBackgroundApp;
- }
-
- void setCurrentBackgroundApp(ComponentName componentName) {
- mCurrentBackgroundApp = componentName;
- }
-
- boolean isBlankActivity(ActivityManager.RunningTaskInfo taskInfo) {
- return mBlankActivityComponent.equals(taskInfo.baseActivity);
- }
-
- boolean isAppGridActivity(TaskInfo taskInfo) {
- return mAppGridActivityComponent.equals(taskInfo.baseActivity);
- }
-
- public Set<ComponentName> getFullScreenActivities() {
- return mFullScreenActivities;
- }
-
- boolean isFullScreenActivity(TaskInfo taskInfo) {
- return mFullScreenActivities.contains(taskInfo.baseActivity);
- }
-
- boolean isNotificationActivity(TaskInfo taskInfo) {
- return mNotificationActivityComponent.equals(taskInfo.baseActivity);
- }
-
- boolean isRecentsActivity(TaskInfo taskInfo) {
- return mRecentsActivityComponent.equals(taskInfo.baseActivity);
- }
-
- boolean shouldIgnoreOpeningForegroundDA(TaskInfo taskInfo) {
- return taskInfo.baseIntent != null && mIgnoreOpeningRootTaskViewComponentsSet.contains(
- taskInfo.baseIntent.getComponent());
+ /**
+ * Refresh {@code mFullScreenActivities} and {@code mBackgroundActivities}.
+ */
+ void refresh() {
+ updateVoicePlateActivityMap();
+ updateBackgroundActivityMap();
}
static boolean isHomeIntent(TaskInfo taskInfo) {
@@ -165,4 +123,193 @@
}
return componentNames;
}
+
+ void updateVoicePlateActivityMap() {
+ mFullScreenActivities.clear();
+ Intent voiceIntent = new Intent(Intent.ACTION_VOICE_ASSIST, /* uri= */ null);
+ List<ResolveInfo> result = mContext.getPackageManager().queryIntentActivitiesAsUser(
+ voiceIntent, PackageManager.MATCH_ALL, ActivityManager.getCurrentUser());
+
+ for (ResolveInfo info : result) {
+ if (info == null || info.activityInfo == null
+ || info.activityInfo.getComponentName() == null) {
+ continue;
+ }
+ if (mFullScreenActivities.add(info.activityInfo.getComponentName())) {
+ logIfDebuggable("adding the following component to show on fullscreen: "
+ + info.activityInfo.getComponentName());
+ }
+ }
+
+ mFullScreenActivities.addAll(convertToComponentNames(mContext.getResources()
+ .getStringArray(R.array.config_fullScreenActivities)));
+ }
+
+ void updateBackgroundActivityMap() {
+ mBackgroundActivities.clear();
+ Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse(STUB_GEO_DATA));
+ List<ResolveInfo> result = mContext.getPackageManager().queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_ALL, ActivityManager.getCurrentUser());
+
+ for (ResolveInfo info : result) {
+ if (info == null || info.activityInfo == null
+ || info.activityInfo.getComponentName() == null) {
+ continue;
+ }
+ mBackgroundActivities.add(info.getComponentInfo().getComponentName());
+ }
+ mBackgroundActivities.addAll(convertToComponentNames(mContext.getResources()
+ .getStringArray(R.array.config_backgroundActivities)));
+ }
+
+ /**
+ * Returns whether the {@code TaskCategoryManager} is ready. If maps intent can be resolved,
+ * the {@code TaskCategoryManager} is ready.
+ */
+ public boolean isReady() {
+ Intent intent = CarLauncherUtils.getMapsIntent(mContext);
+ return intent.resolveActivity(mContext.getPackageManager()) != null;
+ }
+
+ void registerOnApplicationInstallUninstallListener(
+ OnApplicationInstallUninstallListener onApplicationInstallUninstallListener) {
+ mOnApplicationInstallUninstallListeners.add(onApplicationInstallUninstallListener);
+ }
+
+ void unregisterOnApplicationInstallUninstallListener(
+ OnApplicationInstallUninstallListener onApplicationInstallUninstallListener) {
+ mOnApplicationInstallUninstallListeners.remove(onApplicationInstallUninstallListener);
+ }
+
+ boolean isBackgroundApp(TaskInfo taskInfo) {
+ return mBackgroundActivities.contains(taskInfo.baseActivity);
+ }
+
+ boolean isBackgroundApp(ComponentName componentName) {
+ return mBackgroundActivities.contains(componentName);
+ }
+
+ boolean isCurrentBackgroundApp(ComponentName componentName) {
+ return mCurrentBackgroundApp != null && mCurrentBackgroundApp.equals(componentName);
+ }
+
+ ComponentName getCurrentBackgroundApp() {
+ return mCurrentBackgroundApp;
+ }
+
+ void setCurrentBackgroundApp(ComponentName componentName) {
+ mCurrentBackgroundApp = componentName;
+ }
+
+ boolean isAppGridActivity(ComponentName componentName) {
+ return mAppGridActivityComponent.equals(componentName);
+ }
+
+ boolean isAppGridActivity(TaskInfo taskInfo) {
+ return mAppGridActivityComponent.equals(taskInfo.baseActivity);
+ }
+
+ ComponentName getAppGridActivity() {
+ return mAppGridActivityComponent;
+ }
+
+ Set<ComponentName> getFullScreenActivities() {
+ return mFullScreenActivities;
+ }
+
+ Set<ComponentName> getBackgroundActivities() {
+ return mBackgroundActivities;
+ }
+
+ boolean isFullScreenActivity(TaskInfo taskInfo) {
+ return mFullScreenActivities.contains(taskInfo.baseActivity);
+ }
+
+ boolean isNotificationActivity(TaskInfo taskInfo) {
+ return mNotificationActivityComponent.equals(taskInfo.baseActivity);
+ }
+
+ boolean isRecentsActivity(TaskInfo taskInfo) {
+ return mRecentsActivityComponent.equals(taskInfo.baseActivity);
+ }
+
+ boolean shouldIgnoreOpeningForegroundDA(TaskInfo taskInfo) {
+ return taskInfo.baseIntent != null && mIgnoreOpeningRootTaskViewComponentsSet.contains(
+ taskInfo.baseIntent.getComponent());
+ }
+
+ public void onDestroy() {
+ mOnApplicationInstallUninstallListeners.clear();
+ mContext.unregisterReceiver(mApplicationInstallUninstallReceiver);
+ }
+
+ /**
+ * Returns a list of activities that are tracked as background activities with given
+ * {@code packageName}.
+ */
+ List<ComponentName> getBackgroundActivitiesFromPackage(String packageName) {
+ List<ComponentName> list = new ArrayList<>();
+ for (ComponentName componentName : mBackgroundActivities) {
+ if (componentName.getPackageName().equals(packageName)) {
+ list.add(componentName);
+ }
+ }
+ return list;
+ }
+
+ private ApplicationInstallUninstallReceiver registerApplicationInstallUninstallReceiver(
+ Context context) {
+ ApplicationInstallUninstallReceiver
+ installUninstallReceiver = new ApplicationInstallUninstallReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(installUninstallReceiver, filter);
+ return installUninstallReceiver;
+ }
+
+ /**
+ * Listener for application installation and uninstallation.
+ */
+ interface OnApplicationInstallUninstallListener {
+ /**
+ * Invoked when intent with {@link Intent.ACTION_PACKAGE_ADDED) is received.
+ */
+ void onAppInstalled(String packageName);
+ /**
+ * Invoked when intent with {@link Intent.ACTION_PACKAGE_REMOVED}} is received.
+ */
+ void onAppUninstall(String packageName);
+ }
+
+ private class ApplicationInstallUninstallReceiver extends BroadcastReceiver {
+ @MainThread
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ String action = intent.getAction();
+ if (TextUtils.isEmpty(packageName) && TextUtils.isEmpty(action)) {
+ logIfDebuggable(
+ "Invalid intent with packageName=" + packageName + ", action=" + action);
+ // Ignoring empty announcements
+ return;
+ }
+ updateBackgroundActivityMap();
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ for (OnApplicationInstallUninstallListener listener :
+ mOnApplicationInstallUninstallListeners) {
+ listener.onAppInstalled(packageName);
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ for (OnApplicationInstallUninstallListener listener :
+ mOnApplicationInstallUninstallListeners) {
+ listener.onAppUninstall(packageName);
+ }
+ } else {
+ logIfDebuggable("Skip action " + action + " for package" + packageName);
+ }
+ }
+ }
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitAudioCard.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitAudioCard.java
deleted file mode 100644
index f2a1035..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitAudioCard.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.portraitlauncher.homescreen.audio;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.car.carlauncher.homescreen.CardPresenter;
-import com.android.car.carlauncher.homescreen.HomeCardFragment;
-import com.android.car.carlauncher.homescreen.audio.AudioCard;
-import com.android.car.carlauncher.homescreen.audio.AudioFragment;
-import com.android.car.carlauncher.homescreen.audio.InCallModel;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-/**
- * A portrait UI version of {@link AudioCard}
- */
-public class PortraitAudioCard extends AudioCard {
-
- private static final String TAG = "PortraitAudioCard";
- private PortraitHomeAudioCardPresenter mAudioCardPresenter;
- private AudioFragment mAudioCardView;
-
- @Override
- public CardPresenter getCardPresenter() {
- if (mAudioCardPresenter == null) {
- mAudioCardPresenter = new PortraitHomeAudioCardPresenter();
- if (getViewModelProvider() == null) {
- Log.w(TAG, "No ViewModelProvider set. Cannot get PortraitMediaViewModel");
- mAudioCardPresenter.setModels(Collections.unmodifiableList(
- Collections.singletonList(
- new InCallModel(SystemClock.elapsedRealtimeClock()))));
- } else {
- mAudioCardPresenter.setModels(Collections.unmodifiableList(
- Arrays.asList(getViewModelProvider().get(PortraitMediaViewModel.class),
- new InCallModel(SystemClock.elapsedRealtimeClock()))));
- }
- }
- return mAudioCardPresenter;
- }
-
- @Override
- public HomeCardFragment getCardView() {
- if (mAudioCardView == null) {
- mAudioCardView = new AudioFragment();
- getCardPresenter().setView(mAudioCardView);
- mAudioCardView.setPresenter(getCardPresenter());
- }
- return mAudioCardView;
- }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitHomeAudioCardPresenter.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitHomeAudioCardPresenter.java
deleted file mode 100644
index 6c2f469..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitHomeAudioCardPresenter.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.portraitlauncher.homescreen.audio;
-
-import android.view.View;
-
-import com.android.car.carlauncher.homescreen.CardPresenter;
-import com.android.car.carlauncher.homescreen.HomeCardInterface;
-import com.android.car.carlauncher.homescreen.audio.AudioPresenter;
-import com.android.car.carlauncher.homescreen.audio.InCallModel;
-import com.android.car.media.common.PlaybackControlsActionBar;
-
-import java.util.List;
-
-/**
- * A portrait UI version of {@link HomeAudioCardPresenter}
- */
-public class PortraitHomeAudioCardPresenter extends CardPresenter implements AudioPresenter{
- private PortraitMediaViewModel mPortraitMediaViewModel;
- private List<HomeCardInterface.Model> mModelList;
- private HomeCardInterface.Model mCurrentModel;
-
- @Override
- public void setModels(List<HomeCardInterface.Model> models) {
- mModelList = models;
- }
-
- protected List<HomeCardInterface.Model> getModels() {
- return mModelList;
- }
-
-
- /**
- * Called when the View is created
- */
- @Override
- public void onViewCreated() {
- for (HomeCardInterface.Model model : getModels()) {
- if (model.getClass() == PortraitMediaViewModel.class) {
- mPortraitMediaViewModel = (PortraitMediaViewModel) model;
- }
- model.setPresenter(this);
- model.onCreate(getFragment().requireContext());
- }
- }
-
- /**
- * Called when the View is destroyed
- */
- @Override
- public void onViewDestroyed() {
- if (mModelList != null) {
- for (HomeCardInterface.Model model : mModelList) {
- model.onDestroy(getFragment().requireContext());
- }
- }
- }
-
- /**
- * Called when the View is clicked
- */
- @Override
- public void onViewClicked(View v) {
- mCurrentModel.onClick(v);
- }
-
- @Override
- public void onModelUpdated(HomeCardInterface.Model model) {
- // Null card header indicates the model has no content to display
- if (model.getCardHeader() == null) {
- if (mCurrentModel != null && model.getClass() == mCurrentModel.getClass()) {
- // If the model currently on display is updating to empty content, check if there
- // is media content to display. If there is no media content the super method is
- // called with empty content, which hides the card.
- if (mPortraitMediaViewModel != null
- && mPortraitMediaViewModel.getCardHeader() != null) {
- mCurrentModel = mPortraitMediaViewModel;
- super.onModelUpdated(mPortraitMediaViewModel);
- return;
- }
- } else {
- // Otherwise, another model is already on display, so don't update with this
- // empty content since that would hide the card.
- return;
- }
- } else if (mCurrentModel != null && mCurrentModel.getClass() == InCallModel.class
- && model.getClass() != InCallModel.class) {
- // If the Model has content, check if currentModel on display is an ongoing phone call.
- // If there is any ongoing phone call, do not update the View
- // if the model trying to update View is NOT a phone call.
- return;
- }
- mCurrentModel = model;
- super.onModelUpdated(model);
- }
-
- public void initializeControlsActionBar(View actionBar) {
- ((PlaybackControlsActionBar) actionBar).setModel(
- mPortraitMediaViewModel.getPlaybackViewModel(),
- getFragment().getViewLifecycleOwner());
- }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitMediaViewModel.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitMediaViewModel.java
deleted file mode 100644
index ff1b3bc..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/homescreen/audio/PortraitMediaViewModel.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.portraitlauncher.homescreen.audio;
-
-import static android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT;
-
-import android.app.Application;
-import android.car.media.CarMediaIntents;
-import android.content.Intent;
-import android.view.View;
-
-import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
-import com.android.car.media.common.source.MediaSource;
-
-/**
- * A portrait UI version of {@link MediaViewModel}
- */
-public class PortraitMediaViewModel extends MediaViewModel {
- public final MediaIntentRouter mMediaIntentRouter;
-
- public PortraitMediaViewModel(Application application) {
- super(application);
- mMediaIntentRouter = MediaIntentRouter.getInstance();
- }
-
- @Override
- public void onClick(View v) {
- MediaSource mediaSource = getMediaSourceViewModel().getPrimaryMediaSource().getValue();
- Intent intent = new Intent(CarMediaIntents.ACTION_MEDIA_TEMPLATE);
- if (mediaSource != null) {
- intent.putExtra(EXTRA_MEDIA_COMPONENT,
- mediaSource.getBrowseServiceComponentName().flattenToString());
- }
- mMediaIntentRouter.handleMediaIntent(intent);
- }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/BackgroundSurfaceView.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/BackgroundSurfaceView.java
index a26afb0..286cbec 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/BackgroundSurfaceView.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/BackgroundSurfaceView.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -40,6 +42,9 @@
// The color used in the surface view.
private int mColor;
+ // The Text at the center of the surface view.
+ private String mText;
+
public BackgroundSurfaceView(Context context) {
this(context, null);
}
@@ -71,13 +76,13 @@
getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
- drawColorOnSurface(holder);
+ drawSurface(holder);
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
int height) {
- drawColorOnSurface(holder);
+ drawSurface(holder);
}
@Override
@@ -87,12 +92,23 @@
});
}
- private void drawColorOnSurface(SurfaceHolder holder) {
+ private void drawSurface(SurfaceHolder holder) {
Canvas canvas = holder.lockCanvas();
if (canvas == null) {
return;
}
canvas.drawColor(mColor);
+
+ if (mText != null) {
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(20);
+ paint.setTextAlign(Paint.Align.CENTER);
+ float xPos = (canvas.getWidth() / 2f);
+ float yPos = (canvas.getHeight() / 2f - ((paint.descent() + paint.ascent()) / 2));
+ canvas.drawText(mText, xPos, yPos, paint);
+ }
+
holder.unlockCanvasAndPost(canvas);
}
@@ -100,14 +116,20 @@
public void setFixedColor(int color) {
mColor = color;
mUseFixedColor = true;
- drawColorOnSurface(getHolder());
+ drawSurface(getHolder());
+ }
+
+ /** Sets the fixed color and centered text on the surface view */
+ public void setFixedColorAndText(int color, String text) {
+ mText = text;
+ setFixedColor(color);
}
/** refreshes the color of the surface view if needed. */
public void refresh(Resources.Theme theme) {
if (!mUseFixedColor) {
mColor = getResources().getColor(R.color.car_background, theme);
- drawColorOnSurface(getHolder());
+ drawSurface(getHolder());
}
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanel.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanel.java
index 331bf83..62dd6e4 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanel.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanel.java
@@ -18,6 +18,7 @@
import android.annotation.SuppressLint;
import android.app.TaskInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
@@ -27,7 +28,6 @@
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -42,7 +42,9 @@
import com.android.car.portraitlauncher.panel.animation.FadeOutPanelAnimator;
import com.android.car.portraitlauncher.panel.animation.FullScreenPanelAnimator;
import com.android.car.portraitlauncher.panel.animation.OpenPanelAnimator;
+import com.android.car.portraitlauncher.panel.animation.OpenPanelWithIconAnimator;
import com.android.car.portraitlauncher.panel.animation.PanelAnimator;
+
/**
* A view container used to display CarTaskViews.
*
@@ -164,6 +166,9 @@
/** The drag threshold after which the panel transitions to the close mode. */
private final int mDragThreshold;
+ /** The top margin for task view panel. */
+ private final int mPanelTopMargin;
+
/** The height of the grip bar. */
private int mGripBarHeight;
@@ -176,8 +181,8 @@
/** Internal container of the {@code CarTaskView}. */
private ViewGroup mTaskViewContainer;
- /** A view that is shown on top of the task view and used to fake the fade effect. */
- private View mTaskViewOverlay;
+ /** A view that is shown on top of the task view and used to improve visual effects. */
+ private TaskViewPanelOverlay mTaskViewOverlay;
/** The {@code CarTaskView} embedded in this panel. This is the main content of the panel. */
private CarTaskView mTaskView;
@@ -211,6 +216,7 @@
super(context, attrs, defStyleAttr, defStyleRes);
mDragThreshold = (int) getResources().getDimension(R.dimen.panel_drag_threshold);
+ mPanelTopMargin = (int) getResources().getDimension(R.dimen.panel_default_top_margin);
mOpenState = new State(/* hasGripBar = */ true, /* isVisible = */ true,
/* isFullScreen */false, /* hasToolBar = */ false,
@@ -244,6 +250,11 @@
return mActiveState == mOpenState;
}
+ /** Whether the panel is in the full screen state. */
+ public boolean isFullScreen() {
+ return mActiveState.isFullScreen();
+ }
+
/** Whether the panel is visible */
public boolean isVisible() {
return mActiveState.isVisible();
@@ -266,6 +277,13 @@
setActiveState(mOpenState, animator);
}
+ /** Transitions the panel into the open state with overlay and centered icon. */
+ public void openPanelWithIcon() {
+ PanelAnimator animator = new OpenPanelWithIconAnimator(this, mOpenState.mBounds,
+ mTaskViewOverlay);
+ setActiveState(mOpenState, animator);
+ }
+
/** Transitions the panel into the close state. */
public void closePanel() {
closePanel(/* animated= */ true);
@@ -295,11 +313,15 @@
/** Transitions the panel into the close state using the fade-out animation. */
public void fadeOutPanel() {
PanelAnimator animator =
- new FadeOutPanelAnimator(this, mTaskViewOverlay, mTaskView, mCloseState.mBounds);
+ new FadeOutPanelAnimator(this, mTaskViewOverlay, mTaskView, mCloseState.mBounds,
+ mCloseState.mBounds.top);
setActiveState(mCloseState, animator);
}
- /** Transitions the panel into the full screen state. */
+ /**
+ * Transitions the panel into the full screen state. During
+ * transition,{@link mTaskViewOverlay} shows with given {@code drawable} at the center.
+ */
public void openFullScreenPanel(boolean animated, boolean showToolBar, int bottomAdjustment) {
mFullScreenState.mHasToolBar = showToolBar;
mFullScreenState.mBounds.bottom = ((ViewGroup) getParent()).getHeight() - bottomAdjustment;
@@ -311,6 +333,11 @@
mOnStateChangeListener = listener;
}
+ /** Sets the component that {@link mTaskViewOverlay} covers */
+ public void setComponentName(ComponentName componentName) {
+ mTaskViewOverlay.setComponentName(componentName);
+ }
+
/**
* Returns the grip bar bounds of the current state.
*
@@ -352,7 +379,7 @@
public void refresh(Resources.Theme theme) {
int backgroundColor = getResources().getColor(R.color.car_background, theme);
mTaskViewContainer.setBackgroundColor(backgroundColor);
- mTaskViewOverlay.setBackgroundColor(backgroundColor);
+ mTaskViewOverlay.refresh();
mGripBar.refresh(theme);
mBackgroundSurfaceView.refresh(theme);
}
@@ -475,28 +502,11 @@
mToolBarView.updateToolBarContentVisibility(mActiveState.hasToolBar() && isVisible);
}
- /**
- * Set current {@link TaskInfo} for the panel.
- */
- public void setCurrentTask(TaskInfo task) {
- mCurrentTask = task;
- }
-
- /**
- * Get current {@link TaskInfo} from the panel.
- */
- @Nullable
- public TaskInfo getCurrentTask() {
- return mActiveState.isVisible() ? mCurrentTask : null;
- }
-
private void recalculateBounds() {
int parentWidth = ((ViewGroup) getParent()).getWidth();
int parentHeight = ((ViewGroup) getParent()).getHeight();
- int panelHeight = parentWidth + mOpenState.mInsets.bottom + mGripBarHeight + 1;
- int panelTop = Math.max(0, parentHeight - panelHeight);
- mOpenState.mBounds.set(0, panelTop, parentWidth, parentHeight);
+ mOpenState.mBounds.set(0, mPanelTopMargin + mGripBarHeight, parentWidth, parentHeight);
mCloseState.mBounds.set(0, parentHeight, parentWidth,
parentHeight + mOpenState.mBounds.height());
mFullScreenState.mBounds.set(0, 0, parentWidth, parentHeight);
@@ -535,29 +545,31 @@
updateTaskViewWindowBounds();
if (animated) {
- // Hide toolbar before animation if toState doesn't have toolbar for better
- // animation
- if (!toState.hasToolBar()) {
- mToolBarView.setVisibility(GONE);
+ // Change toolbar and grip bar visibilities before the animation for better animation.
+ if (toState.hasToolBar() != fromState.hasToolBar()) {
+ mToolBarView.setVisibility(toState.hasToolBar() ? VISIBLE : GONE);
}
- post(() -> {
- animator.animate(() -> {
- mGripBar.setVisibility(toState.hasGripBar() ? VISIBLE : GONE);
- mToolBarView.setVisibility(toState.hasToolBar() ? VISIBLE : GONE);
- mBackgroundSurfaceView.setVisibility(
- toState.hasBackgroundSurfaceView() ? VISIBLE : GONE);
- updateBounds(mActiveState.mBounds);
- onStateChangeEnd(fromState, toState, /* animated= */ true);
- }
- );
- });
+
+ if (toState.hasGripBar() != fromState.hasGripBar()) {
+ mGripBar.setVisibility(toState.hasGripBar() ? VISIBLE : GONE);
+ }
+
+ post(() -> animator.animate(() -> {
+ mGripBar.setVisibility(mActiveState.hasGripBar() ? VISIBLE : GONE);
+ mToolBarView.setVisibility(mActiveState.hasToolBar() ? VISIBLE : GONE);
+ mBackgroundSurfaceView.setVisibility(
+ mActiveState.hasBackgroundSurfaceView() ? VISIBLE : GONE);
+ updateBounds(mActiveState.mBounds);
+ onStateChangeEnd(fromState, mActiveState, /* animated= */ true);
+ }));
} else {
- mGripBar.setVisibility(toState.hasGripBar() ? VISIBLE : GONE);
- mToolBarView.setVisibility(toState.hasToolBar() ? VISIBLE : GONE);
+ mGripBar.setVisibility(mActiveState.hasGripBar() ? VISIBLE : GONE);
+ mToolBarView.setVisibility(mActiveState.hasToolBar() ? VISIBLE : GONE);
mBackgroundSurfaceView.setVisibility(
- toState.hasBackgroundSurfaceView() ? VISIBLE : GONE);
+ mActiveState.hasBackgroundSurfaceView() ? VISIBLE : GONE);
updateBounds(mActiveState.mBounds);
- onStateChangeEnd(fromState, toState, /* animated= */ false);
+ mTaskViewOverlay.setVisibility(GONE);
+ onStateChangeEnd(fromState, mActiveState, /* animated= */ false);
}
}
@@ -604,6 +616,6 @@
private FullScreenPanelAnimator createFullScreenPanelAnimator() {
Point offset = new Point(mOpenState.mBounds.left, mOpenState.mBounds.top);
Rect bounds = mFullScreenState.mBounds;
- return new FullScreenPanelAnimator(this, bounds, offset);
+ return new FullScreenPanelAnimator(this, bounds, offset, mTaskViewOverlay);
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanelOverlay.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanelOverlay.java
new file mode 100644
index 0000000..33a072c
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/TaskViewPanelOverlay.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.panel;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.car.portraitlauncher.R;
+
+/**
+ * A view that is shown on top of the task view in {@link TaskViewPanel} to improve visual effects.
+ */
+public class TaskViewPanelOverlay extends ConstraintLayout {
+ private static final String TAG = TaskViewPanelOverlay.class.getSimpleName();
+
+ private ImageView mBackground;
+ private ImageView mIcon;
+ private ComponentName mComponentName;
+
+ public TaskViewPanelOverlay(@NonNull Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public TaskViewPanelOverlay(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public TaskViewPanelOverlay(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+ }
+
+ public TaskViewPanelOverlay(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mBackground = findViewById(R.id.task_view_overlay_icon_background);
+ mIcon = findViewById(R.id.task_view_overlay_icon);
+ }
+
+ /**
+ * Sets the visibility of the overlay to visible and set the centered icon state according to
+ * {@code withIcon}.
+ */
+ public void show(boolean withIcon) {
+ setVisibility(VISIBLE);
+ setIconEnabled(withIcon);
+ }
+
+ /** Sets the visibility of the overlay to gone and disable the centered icon. */
+ public void hide() {
+ setVisibility(GONE);
+ setIconEnabled(/* enableIcon= */ false);
+ }
+
+ /** Sets {@code componentName} that used to retrieve the centered icon. */
+ void setComponentName(ComponentName componentName) {
+ mComponentName = componentName;
+ }
+
+ /** Refreshes the overlay according to current theme. */
+ void refresh() {
+ int backgroundColor = getResources().getColor(R.color.car_background, mContext.getTheme());
+ setBackgroundColor(backgroundColor);
+ Drawable iconBackgroundDrawable = getResources().getDrawable(R.drawable.app_icon_background,
+ mContext.getTheme());
+ mBackground.setBackground(iconBackgroundDrawable);
+ }
+
+ /**
+ * Sets the state of the centered icon.
+ *
+ * @param enabled True if the icon is enabled, false otherwise.
+ */
+ private void setIconEnabled(boolean enabled) {
+ mIcon.setBackground(enabled ? getApplicationIcon(mComponentName) : null);
+ mIcon.setVisibility(enabled ? VISIBLE : GONE);
+ mBackground.setVisibility(enabled ? VISIBLE : GONE);
+ }
+
+ /** Retrieve the icon associated with the given {@code componentName}. */
+ private Drawable getApplicationIcon(ComponentName componentName) {
+ if (componentName == null) {
+ return null;
+ }
+
+ Drawable icon = null;
+ try {
+ icon = mContext.getPackageManager().getApplicationIcon(componentName.getPackageName());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Fail to get application icon. ", e);
+ }
+ return icon;
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FadeOutPanelAnimator.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FadeOutPanelAnimator.java
index 41074d6..dbdd364 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FadeOutPanelAnimator.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FadeOutPanelAnimator.java
@@ -17,7 +17,6 @@
package com.android.car.portraitlauncher.panel.animation;
import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
import android.graphics.Rect;
import android.view.View;
@@ -26,6 +25,8 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.car.portraitlauncher.panel.TaskViewPanelOverlay;
+
/**
* A {@code PanelAnimator} to animate the panel into the open state using the fade-in animation.
*/
@@ -40,9 +41,10 @@
private static final float FADE_OUT_ALPHA = 0;
private static final float FADE_IN_ALPHA = 1;
- private final View mOverlay;
+ private final TaskViewPanelOverlay mOverlay;
private final View mTaskView;
private final Rect mBounds;
+ private final float mOffScreenYPosition;
private ViewPropertyAnimator mOverlayAnimator;
private ViewPropertyAnimator mTaskViewAnimator;
@@ -54,20 +56,24 @@
* the {@code TaskView}.
* @param taskView The task view of the panel.
* @param toBounds The final bounds of the panel within its parent
+ * @param offScreenYPosition Y value that can be applied to a panel to get it off the screen.
+ * This is needed to hide panels during the animation.
*/
- public FadeOutPanelAnimator(ViewGroup panel, View overlay, View taskView,
- Rect toBounds) {
+ public FadeOutPanelAnimator(ViewGroup panel, TaskViewPanelOverlay overlay, View taskView,
+ Rect toBounds, float offScreenYPosition) {
super(panel);
mOverlay = overlay;
mTaskView = taskView;
mBounds = toBounds;
+ mOffScreenYPosition = offScreenYPosition;
}
@Override
public void animate(Runnable endAction) {
- mOverlay.setVisibility(VISIBLE);
+ mOverlay.show(/* withIcon= */ false);
mOverlay.setAlpha(FADE_OUT_ALPHA);
- // First fade in the overlay and then fade-out the whole panel.
+ // First fade in the overlay with scaling down the task view and then fade-out the whole
+ // panel.
// This is necessary since we cannot fade the task views.
mOverlayAnimator = mOverlay.animate().alpha(FADE_IN_ALPHA)
.setDuration(OVERLAY_FADE_IN_DURATION)
@@ -75,9 +81,9 @@
mPanel.animate().alpha(FADE_OUT_ALPHA).setDuration(PANEL_FADE_OUT_DURATION)
.withEndAction(() -> {
mOverlay.setVisibility(GONE);
- mTaskView.setVisibility(VISIBLE);
mTaskView.setScaleX(INITIAL_SCALE);
mTaskView.setScaleY(INITIAL_SCALE);
+ mTaskView.setTranslationY(0);
mPanel.setAlpha(FADE_IN_ALPHA);
endAction.run();
});
@@ -89,7 +95,10 @@
// Restore the initial scale
mTaskView.setScaleX(INITIAL_SCALE);
mTaskView.setScaleY(INITIAL_SCALE);
- mTaskView.setVisibility(GONE);
+ // Move the task view out of the screen to make it not visible. We cannot reset
+ // the visibility to View.GONE because it causes WM to take a screenshot in a
+ // state where the overlay might be covering the task view.
+ mTaskView.setTranslationY(mOffScreenYPosition - mTaskView.getY());
});
}
@@ -103,9 +112,9 @@
mTaskViewAnimator.cancel();
}
mOverlay.setVisibility(GONE);
- mTaskView.setVisibility(VISIBLE);
mTaskView.setScaleX(INITIAL_SCALE);
mTaskView.setScaleY(INITIAL_SCALE);
+ mTaskView.setTranslationY(0);
mPanel.setAlpha(FADE_IN_ALPHA);
updateBounds(mBounds);
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FullScreenPanelAnimator.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FullScreenPanelAnimator.java
index d98c0f2..f76e9ad 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FullScreenPanelAnimator.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/FullScreenPanelAnimator.java
@@ -19,35 +19,48 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.car.portraitlauncher.panel.TaskViewPanelOverlay;
+
/**
* A {@code PanelAnimator} to animate the panel into the full screen state.
*
* This animator resizes the panel into the given bounds using the full-screen panel animation
- * parameters.
+ * parameters. Covering the panel with overlay and centered icon during animation.
*/
public class FullScreenPanelAnimator extends PanelAnimator {
private static final Interpolator INTERPOLATOR = new PathInterpolator(0.05f, 0.7f, 0.1f, 1);
private static final long DURATION = 400;
+ private static final float OVERLAY_FADE_OUT_END_ALPHA = 0f;
+ private static final float OVERLAY_FADE_OUT_START_ALPHA = 1f;
+ private static final long OVERLAY_FADE_OUT_DURATION = 300;
+ private static final long OVERLAY_FADE_OUT_START_DELAY = 300;
private final Rect mBounds;
private final Point mInitialOffset;
+ private final TaskViewPanelOverlay mOverlay;
private Animation mAnimation;
+ private ViewPropertyAnimator mOverlayAnimator;
/**
* A {@code PanelAnimator} to animate the panel into the full screen state.
*
- * @param panel The panel that should animate
- * @param bounds The final bounds of the panel within its parent
+ * @param panel The panel that should animate
+ * @param bounds The final bounds of the panel within its parent
* @param initialOffset The initial top left corner of the panel in its parent.
+ * @param overlay The overlay view that covers the {@code TaskView}. Used to display
+ * the application icon during animation.
*/
- public FullScreenPanelAnimator(ViewGroup panel, Rect bounds, Point initialOffset) {
+ public FullScreenPanelAnimator(ViewGroup panel, Rect bounds, Point initialOffset,
+ TaskViewPanelOverlay overlay) {
super(panel);
mBounds = bounds;
mInitialOffset = initialOffset;
+ mOverlay = overlay;
}
@Override
@@ -56,8 +69,20 @@
Rect bounds = new Rect(mBounds);
bounds.offset(mInitialOffset.x, mInitialOffset.y);
updateBounds(bounds);
+ mOverlay.show(/* withIcon= */ true);
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
- mAnimation = new BoundsAnimation(mPanel, mBounds, endAction);
+ mAnimation = new BoundsAnimation(mPanel, mBounds, () -> {
+ mOverlayAnimator = mOverlay.animate().alpha(OVERLAY_FADE_OUT_END_ALPHA)
+ .setStartDelay(OVERLAY_FADE_OUT_START_DELAY)
+ .setDuration(OVERLAY_FADE_OUT_DURATION)
+ .withEndAction(() -> {
+ mOverlay.hide();
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
+ endAction.run();
+ });
+ });
+
mAnimation.setInterpolator(INTERPOLATOR);
mAnimation.setDuration(DURATION);
mPanel.post(() -> mPanel.startAnimation(mAnimation));
@@ -68,5 +93,10 @@
if (mAnimation != null) {
mAnimation.cancel();
}
+ if (mOverlayAnimator != null) {
+ mOverlayAnimator.cancel();
+ }
+ mOverlay.hide();
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/OpenPanelWithIconAnimator.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/OpenPanelWithIconAnimator.java
new file mode 100644
index 0000000..5cefa01
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/panel/animation/OpenPanelWithIconAnimator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.portraitlauncher.panel.animation;
+
+import android.graphics.Rect;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+
+import com.android.car.portraitlauncher.panel.TaskViewPanelOverlay;
+
+/**
+ * A {@code PanelAnimator} to animate the panel into the open state with centered icon.
+ *
+ * This animator resizes the panel into the given bounds using the open panel animation
+ * parameters. Covering the panel with overlay and centered icon during animation.
+ */
+public class OpenPanelWithIconAnimator extends OpenPanelAnimator {
+
+ private static final float OVERLAY_FADE_OUT_END_ALPHA = 0f;
+ private static final float OVERLAY_FADE_OUT_START_ALPHA = 1f;
+ private static final long OVERLAY_FADE_OUT_DURATION = 300;
+ private static final long OVERLAY_FADE_OUT_START_DELAY = 300;
+
+ private final TaskViewPanelOverlay mOverlay;
+ private ViewPropertyAnimator mOverlayAnimator;
+
+ /**
+ * A {@code PanelAnimator} to animate the panel into the open state.
+ *
+ * @param panel The panel on which the animator acts.
+ * @param bounds The final bounds that the panel should animate to.
+ * @param overlay The overlay view that covers the {@code TaskView}. Used to cover
+ * the panel during animation.
+ */
+ public OpenPanelWithIconAnimator(ViewGroup panel, Rect bounds,
+ TaskViewPanelOverlay overlay) {
+ super(panel, bounds);
+ mOverlay = overlay;
+ }
+
+ @Override
+ public void animate(Runnable endAction) {
+ mOverlay.show(/* withIcon= */ true);
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
+
+ super.animate(() ->
+ mOverlayAnimator = mOverlay
+ .animate()
+ .alpha(OVERLAY_FADE_OUT_END_ALPHA)
+ .setStartDelay(OVERLAY_FADE_OUT_START_DELAY)
+ .setDuration(OVERLAY_FADE_OUT_DURATION)
+ .withEndAction(() -> {
+ mOverlay.hide();
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
+ endAction.run();
+ })
+ );
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ if (mOverlayAnimator != null) {
+ mOverlayAnimator.cancel();
+ }
+ mOverlay.hide();
+ mOverlay.setAlpha(OVERLAY_FADE_OUT_START_ALPHA);
+ }
+}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitCarRecentsActivity.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitCarRecentsActivity.java
index 8811aac..4bfee84 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitCarRecentsActivity.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitCarRecentsActivity.java
@@ -16,12 +16,10 @@
package com.android.car.portraitlauncher.recents;
-import android.content.Intent;
import android.os.Bundle;
import com.android.car.carlauncher.recents.CarRecentsActivity;
import com.android.car.carlauncher.recents.RecentTasksViewModel;
-import com.android.car.portraitlauncher.homeactivities.CarUiPortraitHomeScreen;
/**
* Recents activity to display list of recent tasks in Car.
@@ -53,9 +51,4 @@
}
super.onResume();
}
-
- @Override
- protected void launchHomeIntent() {
- startActivity(new Intent(this, CarUiPortraitHomeScreen.class));
- }
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitHiddenTaskProvider.java b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitHiddenTaskProvider.java
index 1b377c2..3bf0c60 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitHiddenTaskProvider.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/src/com/android/car/portraitlauncher/recents/PortraitHiddenTaskProvider.java
@@ -35,8 +35,8 @@
public PortraitHiddenTaskProvider(Context context) {
// TODO(b/280647032): use TaskCategoryManager instead of accessing resources directly
- mBackgroundComponentSet = Arrays.stream(context.getResources()
- .getStringArray(R.array.config_backgroundActivities))
+ mBackgroundComponentSet = Arrays.stream(context.getApplicationContext()
+ .getResources().getStringArray(R.array.config_backgroundActivities))
.map(ComponentName::unflattenFromString)
.collect(Collectors.toSet());
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/Android.bp b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/Android.bp
index 4d3b974..c3ec7bf 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/Android.bp
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/Android.bp
@@ -32,7 +32,7 @@
certificate: "platform",
optimize: {
- enabled: false,
+ proguard_flags_files: [":CarSettings_proguard_flags"],
},
privileged: true,
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/preference_dialog_password_edittext.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/preference_dialog_password_edittext.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/preference_dialog_password_edittext.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/preference_dialog_password_edittext.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/settings_recyclerview_default.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/settings_recyclerview_default.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/settings_recyclerview_default.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/settings_recyclerview_default.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/top_level_recyclerview.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/top_level_recyclerview.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout-port/top_level_recyclerview.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/layout/top_level_recyclerview.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values-port/config.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values/config.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values-port/config.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values/config.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values-port/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSettings/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim-port/hvac_close_anim.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim/hvac_close_anim.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim-port/hvac_close_anim.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim/hvac_close_anim.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim-port/hvac_open_anim.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim/hvac_open_anim.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim-port/hvac_open_anim.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/anim/hvac_open_anim.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_seekbar_thumb_selector.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_seekbar_thumb_selector.xml
new file mode 100644
index 0000000..282ecf7
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_seekbar_thumb_selector.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="false"
+ android:color="@color/car_seekbar_primary_color"
+ android:alpha="?attr/android:disabledAlpha"/>
+ <item android:color="@color/car_seekbar_primary_color"/>
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_switch_thumb_selector.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_switch_thumb_selector.xml
new file mode 100644
index 0000000..a10a463
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_switch_thumb_selector.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:color="@color/car_switch_thumb_checked"
+ android:alpha="?attr/android:disabledAlpha" />
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:color="@color/car_switch_thumb_unchecked"
+ android:alpha="?attr/android:disabledAlpha" />
+ <item android:state_checked="true"
+ android:color="@color/car_switch_thumb_checked" />
+ <item android:color="@color/car_switch_thumb_unchecked" />
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_background_color.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_background_color.xml
new file mode 100644
index 0000000..d55b70c
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_background_color.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true"
+ android:state_checked="true"
+ android:color="@color/car_button_toggle_background_checked"/>
+ <item android:state_enabled="true"
+ android:state_checked="false"
+ android:color="@color/car_button_toggle_background_unchecked"/>
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_background_checked"/>
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_background_unchecked"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_icon_fill_color.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_icon_fill_color.xml
new file mode 100644
index 0000000..b738b07
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/qc_toggle_icon_fill_color.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true"
+ android:state_checked="true"
+ android:color="@color/car_button_toggle_foreground_checked"/>
+ <item android:state_enabled="true"
+ android:state_checked="false"
+ android:color="@color/car_button_toggle_foreground_unchecked"/>
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_foreground_checked"/>
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_foreground_unchecked"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port-night/system_bar_icon_color_with_selection.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/system_bar_icon_color_with_selection.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port-night/system_bar_icon_color_with_selection.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-night/system_bar_icon_color_with_selection.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port/car_nav_icon_fill_color_selected.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/car_nav_icon_fill_color_selected.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port/car_nav_icon_fill_color_selected.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/car_nav_icon_fill_color_selected.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_seekbar_thumb_selector.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_seekbar_thumb_selector.xml
new file mode 100644
index 0000000..282ecf7
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_seekbar_thumb_selector.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="false"
+ android:color="@color/car_seekbar_primary_color"
+ android:alpha="?attr/android:disabledAlpha"/>
+ <item android:color="@color/car_seekbar_primary_color"/>
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_switch_thumb_selector.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_switch_thumb_selector.xml
new file mode 100644
index 0000000..a10a463
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_switch_thumb_selector.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:color="@color/car_switch_thumb_checked"
+ android:alpha="?attr/android:disabledAlpha" />
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:color="@color/car_switch_thumb_unchecked"
+ android:alpha="?attr/android:disabledAlpha" />
+ <item android:state_checked="true"
+ android:color="@color/car_switch_thumb_checked" />
+ <item android:color="@color/car_switch_thumb_unchecked" />
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_background_color.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_background_color.xml
new file mode 100644
index 0000000..d55b70c
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_background_color.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true"
+ android:state_checked="true"
+ android:color="@color/car_button_toggle_background_checked"/>
+ <item android:state_enabled="true"
+ android:state_checked="false"
+ android:color="@color/car_button_toggle_background_unchecked"/>
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_background_checked"/>
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_background_unchecked"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_icon_fill_color.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_icon_fill_color.xml
new file mode 100644
index 0000000..b738b07
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_icon_fill_color.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true"
+ android:state_checked="true"
+ android:color="@color/car_button_toggle_foreground_checked"/>
+ <item android:state_enabled="true"
+ android:state_checked="false"
+ android:color="@color/car_button_toggle_foreground_unchecked"/>
+ <item android:state_enabled="false"
+ android:state_checked="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_foreground_checked"/>
+ <item android:state_enabled="false"
+ android:state_checked="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_button_toggle_foreground_unchecked"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_unavailable_color.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_unavailable_color.xml
new file mode 100644
index 0000000..5185f89
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/qc_toggle_unavailable_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:color="@color/car_on_secondary_container"
+ android:alpha="?attr/android:disabledAlpha"/>
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port/system_bar_icon_color_with_selection.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/system_bar_icon_color_with_selection.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color-port/system_bar_icon_color_with_selection.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/color/system_bar_icon_color_with_selection.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mic_light.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mic_light.xml
deleted file mode 100644
index e7f9c95..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mic_light.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/top_system_bar_icon_drawing_size"
- android:height="@dimen/top_system_bar_icon_drawing_size"
- android:viewportWidth="44"
- android:viewportHeight="44">
- <path
- android:pathData="M22 25.6666667C25.0433333 25.6666667 27.4816667 23.21 27.4816667 20.1666667L27.5 9.16666667C27.5 6.12333333 25.0433333 3.66666667 22 3.66666667C18.9566667 3.66666667 16.5 6.12333333 16.5 9.16666667L16.5 20.1666667C16.5 23.21 18.9566667 25.6666667 22 25.6666667ZM31.7166667 20.1666667C31.7166667 25.6666667 27.06 29.5166667 22 29.5166667C16.94 29.5166667 12.2833333 25.6666667 12.2833333 20.1666667L9.16666667 20.1666667C9.16666667 26.4183333 14.1533333 31.5883333 20.1666667 32.4866667L20.1666667 38.5L23.8333333 38.5L23.8333333 32.4866667C29.8466667 31.6066667 34.8333333 26.4366667 34.8333333 20.1666667L31.7166667 20.1666667Z"
- android:fillColor="@color/car_ui_text_color_primary" />
-</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/rounded_corners_cb.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/alert_dialog_bg.xml
similarity index 73%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/rounded_corners_cb.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/alert_dialog_bg.xml
index 3761ef5..5977663 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/rounded_corners_cb.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/alert_dialog_bg.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2023 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.
@@ -16,7 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#f00"/>
- <corners android:topLeftRadius="48dp" android:topRightRadius="48dp"
- android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
-</shape>
+ <solid android:color="@color/car_alert_dialog_background_color"/>
+ <corners android:radius="@dimen/car_portrait_ui_window_rounded_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button1_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_dialog_button1_background.xml
similarity index 100%
copy from car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button1_background.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_dialog_button1_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_dialog_button_background.xml
similarity index 60%
copy from car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_dialog_button_background.xml
index 5564d8e..4d6c8c6 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitLauncher/res/drawable-port/grip_bar_background.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_dialog_button_background.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -18,17 +18,15 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
- <solid android:color="@color/car_background"/>
- <corners android:topLeftRadius="@dimen/corner_radius"
- android:topRightRadius="@dimen/corner_radius"
- android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
+ <solid android:color="@color/car_alert_dialog_action_button_color"/>
+ <corners android:radius="@dimen/car_portrait_ui_button_radius"/>
</shape>
</item>
- <item android:height="@dimen/grip_bar_height" android:width="@dimen/grip_bar_width" android:gravity="center">
- <shape>
- <solid android:color="@color/car_surface_variant"/>
- </shape>
+ <item>
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+ </ripple>
</item>
</layer-list>
-
-
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_apps.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_apps.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_apps.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_apps.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_hvac.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_hvac.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_hvac.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_hvac.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_mic.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_mic.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_mic.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_mic.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_notification.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_notification.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_notification.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_notification.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_status.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_status.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_status.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_status.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_user_icon.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_user_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/car_ic_user_icon.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/car_ic_user_icon.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_1.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_1.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_1.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_1.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_2.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_2.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_2.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_2.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_3.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_3.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_3.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_3.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_4.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_4.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_4.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_4.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_5.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_5.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_5.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_5.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_6.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_6.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_6.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_6.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_7.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_7.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_7.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_7.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_8.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_8.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/fan_speed_seek_bar_thumb_8.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/fan_speed_seek_bar_thumb_8.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_cool_on_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_cool_on_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_cool_on_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_cool_on_bg.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_heat_on_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_heat_on_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_heat_on_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_heat_on_bg.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_off_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_off_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_off_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_off_bg.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_on_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_on_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_button_on_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_button_on_bg.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_cool_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_cool_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_cool_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_cool_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_decrease_button.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_decrease_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_decrease_button.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_decrease_button.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_default_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_default_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_default_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_default_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_heat_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_heat_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_heat_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_heat_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_increase_button.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_increase_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_increase_button.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_increase_button.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_panel_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_panel_bg.xml
similarity index 90%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_panel_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_panel_bg.xml
index 8375d08..b36330e 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/hvac_panel_bg.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/hvac_panel_bg.xml
@@ -25,11 +25,11 @@
</item>
<item>
<shape>
- <solid android:color="@color/car_surface_variant"/>
+ <solid android:color="@color/car_panel_border"/>
<corners android:radius="@dimen/hvac_panel_bg_radius"/>
</shape>
</item>
- <item android:top="@dimen/hvac_panel_stroke_width">
+ <item android:top="@dimen/car_panel_border_width">
<shape>
<solid android:color="@color/hvac_background_color"/>
<corners android:radius="@dimen/hvac_panel_bg_radius"/>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_ac_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_ac_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_ac_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_ac_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_ac_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_ac_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_ac_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_ac_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_feet_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_feet_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_feet_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_feet_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_feet_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_feet_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_feet_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_feet_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_head_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_head_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_head_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_head_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_head_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_head_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_head_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_head_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_windshield_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_windshield_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_windshield_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_windshield_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_windshield_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_windshield_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_airflow_windshield_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_airflow_windshield_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_auto_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_auto_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_auto_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_auto_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_auto_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_auto_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_auto_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_auto_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_baseline_expand_more_24.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_baseline_expand_more_24.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_baseline_expand_more_24.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_baseline_expand_more_24.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_chevron_down.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_chevron_down.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_chevron_down.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_chevron_down.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_chevron_up.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_chevron_up.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_chevron_up.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_chevron_up.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_rear_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_rear_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_rear_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_rear_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_rear_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_rear_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_rear_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_rear_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_windshield_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_windshield_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_windshield_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_windshield_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_windshield_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_windshield_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_defroster_windshield_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_defroster_windshield_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_high.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_high.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_high.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_high.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_low.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_low.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_low.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_low.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_driver_seat_heat_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_driver_seat_heat_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_fan_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_fan_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_fan_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_fan_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_fan_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_fan_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_fan_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_fan_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_heated_steering_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_heated_steering_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_heated_steering_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_heated_steering_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_heated_steering_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_heated_steering_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_heated_steering_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_heated_steering_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_minimize.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_minimize.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_minimize.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_minimize.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_1.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_1.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_1.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_1.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_2.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_2.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_2.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_2.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_3.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_3.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_3.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_3.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_4.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_4.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_4.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_4.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_5.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_5.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_5.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_5.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_6.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_6.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_6.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_6.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_7.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_7.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_7.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_7.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_8.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_8.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_mode_fan_8.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_mode_fan_8.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_high.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_high.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_high.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_high.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_low.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_low.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_low.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_low.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_passenger_seat_heat_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_passenger_seat_heat_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_power_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_power_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_power_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_power_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_power_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_power_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_power_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_power_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_recirculate_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_recirculate_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_recirculate_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_recirculate_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_recirculate_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_recirculate_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_recirculate_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_recirculate_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_sync_off.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_sync_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_sync_off.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_sync_off.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_sync_on.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_sync_on.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/ic_sync_on.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_sync_on.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_selected.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_selected.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_selected.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_selected.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_selected_to_unselected_anim.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_selected_to_unselected_anim.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_selected_to_unselected_anim.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_selected_to_unselected_anim.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_unselected.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_unselected.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_unselected.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_unselected.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_unselected_to_selected_anim.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_unselected_to_selected_anim.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/nav_bar_button_background_unselected_to_selected_anim.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background_unselected_to_selected_anim.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/progress_drawable.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/progress_drawable.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/progress_drawable.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/progress_drawable.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/qc_dialog_button_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_dialog_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/qc_dialog_button_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_dialog_button_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_background.xml
new file mode 100644
index 0000000..fd81bd1
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/qc_toggle_background_size"
+ android:height="@dimen/qc_toggle_background_size"
+ android:start="@dimen/qc_toggle_background_padding"
+ android:top="@dimen/qc_toggle_background_padding"
+ android:drawable="@drawable/qc_toggle_button_background">
+ </item>
+ <item android:width="@dimen/qc_toggle_rotary_highlight_size"
+ android:height="@dimen/qc_toggle_rotary_highlight_size"
+ android:drawable="@drawable/qc_toggle_rotary_background"/>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_button_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_button_background.xml
new file mode 100644
index 0000000..371b6ca
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_button_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_checked="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/qc_toggle_background_color" />
+ <corners android:radius="@dimen/qc_toggle_background_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/qc_toggle_background_color" />
+ </shape>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_unavailable_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_unavailable_background.xml
new file mode 100644
index 0000000..fd81bd1
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/qc_toggle_unavailable_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/qc_toggle_background_size"
+ android:height="@dimen/qc_toggle_background_size"
+ android:start="@dimen/qc_toggle_background_padding"
+ android:top="@dimen/qc_toggle_background_padding"
+ android:drawable="@drawable/qc_toggle_button_background">
+ </item>
+ <item android:width="@dimen/qc_toggle_rotary_highlight_size"
+ android:height="@dimen/qc_toggle_rotary_highlight_size"
+ android:drawable="@drawable/qc_toggle_rotary_background"/>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/status_icon_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/status_icon_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/status_icon_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/status_icon_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/status_icon_panel_bg.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/status_icon_panel_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/status_icon_panel_bg.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/status_icon_panel_bg.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/system_bar_background_pill.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/system_bar_background_pill.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/system_bar_background_pill.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/system_bar_background_pill.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/system_bar_background_square.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/system_bar_background_square.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/system_bar_background_square.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/system_bar_background_square.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/user_avatar_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/user_avatar_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable-port/user_avatar_background.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/user_avatar_background.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/text_toast.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/text_toast.xml
deleted file mode 100644
index 9e76263..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/text_toast.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:maxWidth="@*android:dimen/toast_width"
- android:background="@android:drawable/toast_frame"
- android:elevation="@*android:dimen/toast_elevation"
- android:paddingStart="@dimen/car_portrait_ui_toast_margin"
- android:paddingEnd="@dimen/car_portrait_ui_toast_margin"
- android:paddingTop="@dimen/car_portrait_ui_toast_margin"
- android:paddingBottom="@dimen/car_portrait_ui_toast_margin"
- android:layout_marginBottom="@dimen/car_portrait_ui_toast_bottom_margin">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_portrait_ui_toast_icon_size"
- android:layout_height="@dimen/car_portrait_ui_toast_icon_size"
- android:layout_marginEnd="@dimen/car_portrait_ui_toast_margin"/>
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:textAppearance="@style/TextAppearance.Car.Toast"/>
-</LinearLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/adjustable_temperature_view.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/adjustable_temperature_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/adjustable_temperature_view.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/adjustable_temperature_view.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/alert_dialog_systemui.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/alert_dialog_systemui.xml
new file mode 100644
index 0000000..6df8578
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/alert_dialog_systemui.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<!-- This is the copy of the files at packages/services/Car/car_product/car_ui_portrait/rro/android/res/layout-car/
+ This layout controls the dialog shown by the SysUI and the other one is the platform default. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <include layout="@*android:layout/car_alert_dialog" />
+</LinearLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/camera_privacy_chip.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/camera_privacy_chip.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/camera_privacy_chip.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/camera_privacy_chip.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_bottom_system_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_bottom_system_bar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_qc_entry_points_button.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_qc_entry_points_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_qc_entry_points_button.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_qc_entry_points_button.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_top_system_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_top_system_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_top_system_bar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_top_system_bar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_top_system_bar_unprovisioned.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_top_system_bar_unprovisioned.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/car_top_system_bar_unprovisioned.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_top_system_bar_unprovisioned.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/default_status_icon.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/default_status_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/default_status_icon.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/default_status_icon.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/fan_direction.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/fan_direction.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/fan_direction.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/fan_direction.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/hvac_panel.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/hvac_panel.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/hvac_panel.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/hvac_panel.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/hvac_panel_handle_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/hvac_panel_handle_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/hvac_panel_handle_bar.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/hvac_panel_handle_bar.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/keyguard_container.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/keyguard_container.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/keyguard_container.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/keyguard_container.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/loading_screen.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/loading_screen.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/loading_screen.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/loading_screen.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/mic_privacy_chip.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/mic_privacy_chip.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/mic_privacy_chip.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/mic_privacy_chip.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/qc_profile_switcher.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/qc_profile_switcher.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/qc_profile_switcher.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/qc_profile_switcher.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/sensor_use_started_title.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/sensor_use_started_title.xml
new file mode 100644
index 0000000..493d756
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/sensor_use_started_title.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_alert_dialog_padding"
+ android:orientation="horizontal"
+ android:gravity="center">
+ <ImageView
+ android:layout_width="@dimen/car_ui_dialog_icon_size"
+ android:layout_height="@dimen/car_ui_dialog_icon_size"
+ android:id="@+id/sensor_use_camera_icon"
+ android:src="@*android:drawable/perm_group_camera"
+ android:tint="@color/car_on_surface"
+ android:visibility="gone" />
+ <ImageView
+ android:layout_width="@dimen/car_ui_dialog_icon_size"
+ android:layout_height="@dimen/car_ui_dialog_icon_size"
+ android:id="@+id/sensor_use_microphone_icon"
+ android:src="@*android:drawable/perm_group_microphone"
+ android:tint="@color/car_on_surface"
+ android:visibility="gone" />
+ </LinearLayout>
+ <com.android.internal.widget.DialogTitle
+ android:id="@+id/sensor_use_started_title_message"
+ style="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"/>
+</LinearLayout>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/sysui_overlay_window.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/sysui_overlay_window.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout-port/sysui_overlay_window.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/sysui_overlay_window.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/text_toast.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/text_toast.xml
new file mode 100644
index 0000000..def1864
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/text_toast.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/car_portrait_ui_toast_bottom_margin">
+ <LinearLayout
+ android:layout_width="@*android:dimen/toast_width"
+ android:layout_height="wrap_content"
+ android:gravity="center">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:padding="@dimen/car_portrait_ui_toast_margin">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/car_portrait_ui_toast_icon_size"
+ android:layout_height="@dimen/car_portrait_ui_toast_icon_size"
+ android:layout_marginEnd="@dimen/car_portrait_ui_toast_icon_margin"/>
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.Car.Toast"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-night/colors.xml
similarity index 79%
copy from car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml
copy to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-night/colors.xml
index 3b653f3..680d8e0 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-night/colors.xml
@@ -16,6 +16,12 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <color name="title_bar_display_area_handle_bar_color">#5f6368</color>
+ <color name="title_bar_display_area_background_color">#000000</color>
+
+ <!-- Quick Controls -->
+ <color name="qc_start_icon_color">@color/car_on_surface</color>
+
<!-- ****** Seekbar colors ***** -->
<color name="car_seekbar_primary_color">@color/car_on_primary_container</color>
@@ -25,13 +31,10 @@
<color name="car_button_toggle_foreground_checked">@color/car_on_primary</color>
<color name="car_button_toggle_foreground_unchecked">@color/car_secondary</color>
- <!-- ****** Button colors ***** -->
- <color name="car_button_background">@color/car_secondary_container</color>
- <color name="car_button_foreground">@color/car_on_secondary_container</color>
-
<!-- ****** Switch colors ***** -->
- <color name="car_switch_thumb_checked">@color/car_primary</color>
+ <color name="car_switch_thumb_checked">@color/car_on_primary</color>
<color name="car_switch_thumb_unchecked">@color/car_secondary</color>
- <color name="car_switch_track_checked">@color/car_on_primary</color>
+ <color name="car_switch_track_checked">@color/car_primary</color>
<color name="car_switch_track_unchecked">@color/car_secondary_container</color>
+
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port-night/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port-night/colors.xml
deleted file mode 100644
index 2692aea..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port-night/colors.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <color name="title_bar_display_area_handle_bar_color">#5f6368</color>
- <color name="title_bar_display_area_background_color">#000000</color>
-
- <color name="privacy_chip_light_icon_color">#ffffff</color>
- <color name="privacy_chip_dark_icon_color">#000000</color>
-</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/config.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/config.xml
deleted file mode 100644
index e5f2e41..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/config.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<resources>
- <!-- Car System UI's OverlayViewsMediator.
- Whenever a new class is added, make sure to also add that class to OverlayWindowModule. -->
- <string-array name="config_carSystemUIOverlayViewsMediators" translatable="false">
- <item>com.android.systemui.car.hvac.AutoDismissHvacPanelOverlayViewMediator</item>
- <item>com.android.systemui.car.keyguard.CarKeyguardOverlayViewMediator</item>
- <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
- <item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item>
- <!-- add the below line to display welcome screen -->
- <item>com.android.systemui.car.loading.LoadingViewMediator</item>
- </string-array>
-
- <integer name="hvac_num_fan_speeds">8</integer>
-
- <integer name="config_hvacAutoDismissDurationMs">15000</integer>
-
- <!-- Allow foreground DA to have rounded corner -->
- <bool name="config_enableRoundedCornerForForegroundDisplayArea">false</bool>
-
- <string-array name="config_systemUIServiceComponentsInclude" translatable="false">
- <item>com.android.systemui.car.systembar.CarSystemBar</item>
- <item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier
- </item>
- <item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
- <item>com.android.systemui.car.toast.CarToastUI</item>
- <item>com.android.systemui.car.volume.VolumeUI</item>
- <item>com.android.systemui.car.cluster.ClusterDisplayController</item>
- <item>com.android.systemui.car.displayarea.DisplayAreaComponent</item>
- </string-array>
-
- <string name="config_notificationCenterActivity" translatable="false">
- com.android.car.notification/.CarNotificationCenterActivity
- </string>
-
- <string-array name="config_readOnlyIconControllers" translatable="false">
- <item>com.android.systemui.car.statusicon.ui.StatusBarSensorInfoController</item>
- </string-array>
-
- <string name="config_VoiceAssistantActivity" translatable="false">
- com.google.android.carassistant/com.google.android.apps.gsa.binaries.auto.app.voiceplate.VoicePlateActivity
- </string>
-</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/dimens.xml
deleted file mode 100644
index 418aee2..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/dimens.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<resources>
-
- <dimen name="car_quick_controls_footer_button_vertical_margin">10dp</dimen>
- <dimen name="car_quick_controls_footer_button_horizontal_margin">24dp</dimen>
- <dimen name="car_quick_controls_footer_button_min_height">56dp</dimen>
- <dimen name="car_quick_controls_footer_button_radius">@dimen/car_portrait_ui_button_radius</dimen>
- <dimen name="car_quick_controls_entry_points_start_margin">24dp</dimen>
- <dimen name="car_profile_switcher_padding_top">16dp</dimen>
- <dimen name="car_profile_quick_controls_panel_width">700dp</dimen>
-
- <dimen name="clock_elevation">5dp</dimen>
-
- <dimen name="hvac_container_padding">32dp</dimen>
- <dimen name="hvac_panel_handle_bar_container_height">16dp</dimen>
- <dimen name="hvac_panel_handle_bar_container_width">728dp</dimen>
- <dimen name="hvac_panel_handle_bar_height">6dp</dimen>
- <dimen name="hvac_panel_handle_bar_margin_top">17dp</dimen>
- <dimen name="hvac_panel_handle_bar_width">120dp</dimen>
- <dimen name="hvac_panel_bg_radius">@dimen/car_portrait_ui_window_rounded_corner_radius</dimen>
- <dimen name="hvac_panel_stroke_width">2dp</dimen>
- <dimen name="hvac_panel_off_button_radius">24dp</dimen>
- <dimen name="hvac_panel_on_button_radius">24dp</dimen>
- <dimen name="hvac_panel_seek_bar_radius">44dp</dimen>
- <dimen name="hvac_panel_full_expanded_height">392dp</dimen>
- <dimen name="hvac_panel_title_margin">36dp</dimen>
- <dimen name="hvac_panel_buttons_guideline">@dimen/hvac_panel_handle_bar_container_height</dimen>
- <dimen name="hvac_panel_icon_dimen">48dp</dimen>
- <dimen name="hvac_panel_tall_icon_dimen">108dp</dimen>
- <dimen name="hvac_panel_wide_icon_dimen">96dp</dimen>
- <dimen name="hvac_panel_button_dimen">88dp</dimen>
- <dimen name="hvac_panel_long_button_dimen">208dp</dimen>
- <dimen name="hvac_panel_airflow_button_width_1">210dp</dimen>
- <dimen name="hvac_panel_airflow_button_width_2">332dp</dimen>
- <dimen name="hvac_panel_slider_width">696dp</dimen>
- <dimen name="hvac_panel_button_external_margin">24dp</dimen>
- <dimen name="hvac_panel_button_external_top_margin">16dp</dimen>
- <dimen name="hvac_panel_button_external_bottom_margin">32dp</dimen>
- <dimen name="hvac_panel_button_internal_margin">32dp</dimen>
- <dimen name="hvac_temperature_text_size">56sp</dimen>
- <dimen name="hvac_temperature_text_padding">12dp</dimen>
- <dimen name="hvac_temperature_text_width">108dp</dimen>
- <dimen name="hvac_temperature_button_size">64dp</dimen>
-
- <item name="hvac_heat_or_cool_off_alpha" format="float" type="dimen">0.3</item>
-
- <dimen name="system_bar_icon_drawing_size">56dp</dimen>
- <dimen name="system_bar_button_size">88dp</dimen>
- <!-- Margin between the system bar buttons -->
- <dimen name="system_bar_button_margin">16dp</dimen>
- <!-- Padding between the system bar button and the icon within it -->
- <dimen name="system_bar_button_padding">16dp</dimen>
- <dimen name="system_bar_button_corner_radius">44dp</dimen>
- <dimen name="system_bar_button_corner_radius_selected">24dp</dimen>
- <dimen name="status_bar_system_icon_spacing">32dp</dimen>
- <dimen name="system_bar_background_pill_size">64dp</dimen>
- <dimen name="system_bar_minimize_icon_height">17dp</dimen>
- <dimen name="system_bar_minimize_icon_width">28dp</dimen>
-
-
- <dimen name="user_avatar_background_radius">13dp</dimen>
-
- <dimen name="privacy_chip_horizontal_padding">0dp</dimen>
- <dimen name="privacy_chip_width">@dimen/top_system_bar_icon_size</dimen>
- <!-- TODO(b/264936974): combine top_system_bar_qc_icon_drawing_size and top_system_bar_icon_drawing_size-->
- <dimen name="top_system_bar_qc_icon_drawing_size">30dp</dimen>
- <dimen name="top_system_bar_icon_drawing_size">42dp</dimen>
- <dimen name="top_system_bar_icon_size">64dp</dimen>
- <dimen name="top_system_bar_icon_horizontal_margin">20dp</dimen>
- <dimen name="top_system_bar_icon_selected_corner">13dp</dimen>
-
- <dimen name="car_status_icon_panel_border_radius">@dimen/car_portrait_ui_window_rounded_corner_radius</dimen>
- <dimen name="car_status_icon_panel_padding_bottom">18dp</dimen>
-
- <dimen name="dialog_button_bar_top_padding">0dp</dimen>
-</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/strings.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/strings.xml
deleted file mode 100644
index 7a55c7c..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/strings.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Format for temperature in the temperature control view (No decimal) -->
- <string name="hvac_temperature_format_fahrenheit" translatable="false">%.0f</string>
-
- <!-- HVAC panel header [CHAR LIMIT=40]-->
- <string name="hvac_panel_header">Comfort controls</string>
-
- <!-- Format for temperature in the temperature control view (No decimal) in fahrenheit -->
- <string name="statusbar_temperature_format_fahrenheit" translatable="false">%.0f\u00B0 F
- </string>
- <!-- Format for temperature in the temperature control view (No decimal) in celsius -->
- <string name="statusbar_temperature_format_celsius" translatable="false">%.0f\u00B0 C</string>
-</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/arrays.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/arrays.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/arrays.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/arrays.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/attrs.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/attrs.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/attrs.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
similarity index 66%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/colors.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
index d7952f4..f25b63f 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
@@ -53,8 +53,8 @@
<color name="title_bar_display_area_background_color">#ffffff</color>
<color name="immersive_chevron_icon_color">@color/title_bar_display_area_handle_bar_color</color>
- <color name="privacy_chip_light_icon_color">@color/car_neutral_90</color>
- <color name="privacy_chip_dark_icon_color">@color/car_neutral_10</color>
+ <color name="privacy_chip_light_icon_color">@color/car_on_surface</color>
+ <color name="privacy_chip_dark_icon_color">@color/car_inverse_on_surface</color>
<color name="car_quick_controls_footer_button_color">@color/car_secondary_container</color>
<color name="car_quick_controls_footer_button_text_color">@color/car_on_secondary_container</color>
@@ -66,4 +66,36 @@
<color name="car_user_switcher_add_user_add_sign_color">@color/car_on_secondary_container</color>
<color name="car_user_switching_dialog_background_color">@color/car_neutral_0</color>
<color name="car_user_switching_dialog_loading_text_color">@color/car_neutral_90</color>
+
+ <!-- ****** Alert dialog colors ***** -->
+ <color name="car_alert_dialog_action_button_color">@color/car_secondary_container</color>
+ <color name="car_alert_dialog_background_color">@color/car_surface_2</color>
+ <color name="car_alert_dialog_message_text_color">@color/car_on_surface</color>
+ <color name="car_alert_dialog_button_text_color">@color/car_on_secondary_container</color>
+ <color name="car_alert_dialog_button1_text_color">@color/car_on_primary</color>
+
+ <!-- ****** Seekbar colors ***** -->
+ <color name="car_seekbar_background_color">@color/car_secondary_container</color>
+ <color name="car_seekbar_primary_color">@color/car_primary</color>
+ <color name="car_seekbar_secondary_color">@color/car_secondary</color>
+
+ <!-- ****** Toggle button colors ***** -->
+ <color name="car_button_toggle_background_checked">@color/car_primary</color>
+ <color name="car_button_toggle_background_unchecked">@color/car_secondary_container</color>
+ <color name="car_button_toggle_foreground_checked">@color/car_on_primary</color>
+ <color name="car_button_toggle_foreground_unchecked">@color/car_on_secondary_container</color>
+
+ <!-- ****** Button colors ***** -->
+ <color name="car_button_background">@color/car_primary_container</color>
+ <color name="car_button_foreground">@color/car_on_primary_container</color>
+
+ <!-- ****** Switch colors ***** -->
+ <color name="car_switch_thumb_checked">@color/car_primary_container</color>
+ <color name="car_switch_thumb_unchecked">@color/car_secondary_container</color>
+ <color name="car_switch_track_checked">@color/car_primary</color>
+ <color name="car_switch_track_unchecked">@color/car_secondary</color>
+
+ <!-- Quick Controls -->
+ <color name="qc_start_icon_color">@color/car_on_surface</color>
+
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/config.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/config.xml
index 588e396..4b1e043 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/config.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/config.xml
@@ -16,10 +16,6 @@
-->
<resources>
- <string name="config_notificationCenterActivity" translatable="false">
- com.android.car.notification/.CarNotificationCenterActivity
- </string>
-
<!-- App drawer system bar button intent -->
<string name="system_bar_app_drawer_intent" translatable="false">intent:#Intent;action=com.android.car.carlauncher.ACTION_APP_GRID;package=com.android.car.portraitlauncher;launchFlags=0x24000000;end</string>
@@ -27,4 +23,47 @@
CarService. CarUiPortrait uses LaunchRootCarTaskView and hence should not register
CarSystemUIProxy. -->
<bool name="config_registerCarSystemUIProxy">false</bool>
+ <!-- Determines whether Recents entry point should be shown / should trigger to open Recents -->
+ <bool name="config_enableRecentsEntryPoint">true</bool>
+
+ <!-- Car System UI's OverlayViewsMediator.
+ Whenever a new class is added, make sure to also add that class to OverlayWindowModule. -->
+ <string-array name="config_carSystemUIOverlayViewsMediators" translatable="false">
+ <item>com.android.systemui.car.hvac.AutoDismissHvacPanelOverlayViewMediator</item>
+ <item>com.android.systemui.car.keyguard.CarKeyguardOverlayViewMediator</item>
+ <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
+ <item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item>
+ <!-- add the below line to display welcome screen -->
+ <item>com.android.systemui.car.loading.LoadingViewMediator</item>
+ </string-array>
+
+ <integer name="hvac_num_fan_speeds">8</integer>
+
+ <integer name="config_hvacAutoDismissDurationMs">15000</integer>
+
+ <!-- Allow foreground DA to have rounded corner -->
+ <bool name="config_enableRoundedCornerForForegroundDisplayArea">false</bool>
+
+ <string-array name="config_systemUIServiceComponentsInclude" translatable="false">
+ <item>com.android.systemui.car.systembar.CarSystemBar</item>
+ <item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier
+ </item>
+ <item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
+ <item>com.android.systemui.car.toast.CarToastUI</item>
+ <item>com.android.systemui.car.volume.VolumeUI</item>
+ <item>com.android.systemui.car.cluster.ClusterDisplayController</item>
+ <item>com.android.systemui.car.displayarea.DisplayAreaComponent</item>
+ </string-array>
+
+ <string name="config_notificationCenterActivity" translatable="false">
+ com.android.car.notification/.CarNotificationCenterActivity
+ </string>
+
+ <string-array name="config_readOnlyIconControllers" translatable="false">
+ <item>com.android.systemui.car.statusicon.ui.StatusBarSensorInfoController</item>
+ </string-array>
+
+ <string name="config_VoiceAssistantActivity" translatable="false">
+ com.google.android.carassistant/com.google.android.apps.gsa.binaries.auto.app.voiceplate.VoicePlateActivity
+ </string>
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
index 5ad5fc1..2a5b9e1 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
@@ -17,4 +17,95 @@
<resources>
<dimen name="statusbar_sensor_text_width">88dp</dimen>
+
+ <dimen name="car_quick_controls_footer_button_vertical_margin">10dp</dimen>
+ <dimen name="car_quick_controls_footer_button_horizontal_margin">24dp</dimen>
+ <dimen name="car_quick_controls_footer_button_min_height">56dp</dimen>
+ <dimen name="car_quick_controls_footer_button_radius">@dimen/car_portrait_ui_button_radius</dimen>
+ <dimen name="car_quick_controls_entry_points_start_margin">24dp</dimen>
+ <dimen name="car_profile_switcher_padding_top">16dp</dimen>
+ <dimen name="car_profile_quick_controls_panel_width">700dp</dimen>
+
+ <dimen name="clock_elevation">5dp</dimen>
+
+ <dimen name="hvac_container_padding">32dp</dimen>
+ <dimen name="hvac_panel_handle_bar_container_height">16dp</dimen>
+ <dimen name="hvac_panel_handle_bar_container_width">728dp</dimen>
+ <dimen name="hvac_panel_handle_bar_height">6dp</dimen>
+ <dimen name="hvac_panel_handle_bar_margin_top">17dp</dimen>
+ <dimen name="hvac_panel_handle_bar_width">120dp</dimen>
+ <dimen name="hvac_panel_bg_radius">@dimen/car_portrait_ui_window_rounded_corner_radius</dimen>
+ <dimen name="hvac_panel_stroke_width">2dp</dimen>
+ <dimen name="hvac_panel_off_button_radius">24dp</dimen>
+ <dimen name="hvac_panel_on_button_radius">24dp</dimen>
+ <dimen name="hvac_panel_seek_bar_radius">44dp</dimen>
+ <dimen name="hvac_panel_full_expanded_height">392dp</dimen>
+ <dimen name="hvac_panel_title_margin">36dp</dimen>
+ <dimen name="hvac_panel_buttons_guideline">@dimen/hvac_panel_handle_bar_container_height</dimen>
+ <dimen name="hvac_panel_icon_dimen">48dp</dimen>
+ <dimen name="hvac_panel_tall_icon_dimen">108dp</dimen>
+ <dimen name="hvac_panel_wide_icon_dimen">96dp</dimen>
+ <dimen name="hvac_panel_button_dimen">88dp</dimen>
+ <dimen name="hvac_panel_long_button_dimen">208dp</dimen>
+ <dimen name="hvac_panel_airflow_button_width_1">210dp</dimen>
+ <dimen name="hvac_panel_airflow_button_width_2">332dp</dimen>
+ <dimen name="hvac_panel_slider_width">696dp</dimen>
+ <dimen name="hvac_panel_button_external_margin">24dp</dimen>
+ <dimen name="hvac_panel_button_external_top_margin">16dp</dimen>
+ <dimen name="hvac_panel_button_external_bottom_margin">32dp</dimen>
+ <dimen name="hvac_panel_button_internal_margin">32dp</dimen>
+ <dimen name="hvac_temperature_text_size">56sp</dimen>
+ <dimen name="hvac_temperature_text_padding">12dp</dimen>
+ <dimen name="hvac_temperature_text_width">108dp</dimen>
+ <dimen name="hvac_temperature_button_size">64dp</dimen>
+
+ <item name="hvac_heat_or_cool_off_alpha" format="float" type="dimen">0.3</item>
+
+ <dimen name="system_bar_icon_drawing_size">56dp</dimen>
+ <dimen name="system_bar_button_size">88dp</dimen>
+ <!-- Margin between the system bar buttons -->
+ <dimen name="system_bar_button_margin">16dp</dimen>
+ <!-- Padding between the system bar button and the icon within it -->
+ <dimen name="system_bar_button_padding">16dp</dimen>
+ <dimen name="system_bar_button_corner_radius">44dp</dimen>
+ <dimen name="system_bar_button_corner_radius_selected">24dp</dimen>
+ <dimen name="status_bar_system_icon_spacing">32dp</dimen>
+ <dimen name="system_bar_background_pill_size">64dp</dimen>
+ <dimen name="system_bar_minimize_icon_height">17dp</dimen>
+ <dimen name="system_bar_minimize_icon_width">28dp</dimen>
+
+
+ <dimen name="user_avatar_background_radius">13dp</dimen>
+
+ <dimen name="privacy_chip_horizontal_padding">0dp</dimen>
+ <dimen name="privacy_chip_width">@dimen/top_system_bar_icon_size</dimen>
+ <!-- TODO(b/264936974): combine top_system_bar_qc_icon_drawing_size and top_system_bar_icon_drawing_size-->
+ <dimen name="top_system_bar_qc_icon_drawing_size">30dp</dimen>
+ <dimen name="top_system_bar_icon_drawing_size">42dp</dimen>
+ <dimen name="top_system_bar_icon_size">64dp</dimen>
+ <dimen name="top_system_bar_icon_horizontal_margin">20dp</dimen>
+ <dimen name="top_system_bar_icon_selected_corner">13dp</dimen>
+
+ <dimen name="car_status_icon_panel_border_radius">@dimen/car_portrait_ui_window_rounded_corner_radius</dimen>
+ <dimen name="car_status_icon_panel_padding_bottom">18dp</dimen>
+
+ <dimen name="dialog_button_bar_top_padding">0dp</dimen>
+
+ <dimen name="pin_view_container_maxHeight">380dp</dimen>
+ <dimen name="pattern_view_container_maxHeight">800dp</dimen>
+
+ <!-- ****** Alert dialog dimens ***** -->
+
+ <!-- Dialog corner radius -->
+ <dimen name="alert_dialog_corner_radius">32dp</dimen>
+ <!-- Dialog button corner radius -->
+ <dimen name="alert_dialog_button_corner_radius">24dp</dimen>
+ <!-- Dialog header size -->
+ <dimen name="car_card_header_height">88dp</dimen>
+ <!-- Dialog image size -->
+ <dimen name="car_alert_dialog_title_image_size">@dimen/car_card_header_height</dimen>
+ <!-- Dialog button layout height -->
+ <dimen name="button_layout_height">88dp</dimen>
+ <!-- Default dialog button margin -->
+ <dimen name="car_alert_dialog_button_margin">16dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/ids.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/ids.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/ids.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/ids.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/integers.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/integers.xml
similarity index 100%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/integers.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/integers.xml
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/strings.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/strings.xml
index 2c0cb55..a2572ed 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/strings.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/strings.xml
@@ -26,4 +26,16 @@
<string name="current_profile_subtitle">Current profile</string>
<string name="loading_welcome_message">AAOS</string>
<string name="loading_action_button_text">Continue</string>
+
+ <!-- Format for temperature in the temperature control view (No decimal) -->
+ <string name="hvac_temperature_format_fahrenheit" translatable="false">%.0f</string>
+
+ <!-- HVAC panel header [CHAR LIMIT=40]-->
+ <string name="hvac_panel_header">Comfort controls</string>
+
+ <!-- Format for temperature in the temperature control view (No decimal) in fahrenheit -->
+ <string name="statusbar_temperature_format_fahrenheit" translatable="false">%.0f\u00B0 F
+ </string>
+ <!-- Format for temperature in the temperature control view (No decimal) in celsius -->
+ <string name="statusbar_temperature_format_celsius" translatable="false">%.0f\u00B0 C</string>
</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/styles.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
similarity index 87%
rename from car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/styles.xml
rename to car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
index b781e2a..4912c0f 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
@@ -59,13 +59,12 @@
<item name="android:textColor">@color/car_on_surface</item>
</style>
- <style name="TextAppearance.QC.Title">
- <item name="android:textSize">@dimen/car_ui_body1_size</item>
+ <style name="TextAppearance.QC.Title" parent="TextAppearance.Car.Body.Large">
+ <item name="android:textColor">@color/car_on_surface</item>
</style>
- <style name="TextAppearance.QC.Subtitle">
- <item name="android:textColor">@color/car_primary</item>
- <item name="android:textSize">@dimen/car_ui_body3_size</item>
+ <style name="TextAppearance.QC.Subtitle" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/car_on_surface_variant</item>
</style>
<style name="QuickControlEntryPointButton">
@@ -74,4 +73,8 @@
<item name="android:layout_width">@dimen/top_system_bar_icon_size</item>
<item name="android:layout_height">@dimen/top_system_bar_icon_size</item>
</style>
+
+ <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
+ <item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
+ </style>
</resources>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaController.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaController.java
index 40c90df..935415b 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaController.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaController.java
@@ -18,6 +18,7 @@
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_COLLAPSE_NOTIFICATION_PANEL;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_HIDE_SYSTEM_BAR_FOR_IMMERSIVE_MODE;
+import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE;
import static com.android.car.caruiportrait.common.service.CarUiPortraitService.INTENT_EXTRA_LAUNCHER_READY;
@@ -96,6 +97,10 @@
mCarFullScreenTouchHandler.enable(requested);
Intent intent = new Intent(REQUEST_FROM_SYSTEM_UI);
intent.putExtra(INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED, requested);
+ if (componentName != null) {
+ intent.putExtra(INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE,
+ componentName.flattenToString());
+ }
mApplicationContext.sendBroadcastAsUser(intent,
new UserHandle(ActivityManager.getCurrentUser()));
}
@@ -180,7 +185,10 @@
mCarDeviceProvisionedController = deviceProvisionedController;
mCarFullScreenTouchHandler = new CarFullScreenTouchHandler(mShellExecutor);
mLoadingViewController = loadingViewController;
- mLoadingViewController.start();
+ if (applicationContext.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT) {
+ mLoadingViewController.start();
+ }
BroadcastReceiver taskViewReadyReceiver = new BroadcastReceiver() {
@Override
@@ -251,10 +259,8 @@
}
boolean hideSystemBar = intent.getBooleanExtra(
INTENT_EXTRA_HIDE_SYSTEM_BAR_FOR_IMMERSIVE_MODE, false);
- if (hideSystemBar == mIsImmersive) {
- mCarUiDisplaySystemBarsController.requestImmersiveMode(
- mApplicationContext.getDisplayId(), hideSystemBar);
- }
+ mCarUiDisplaySystemBarsController.requestImmersiveMode(
+ mApplicationContext.getDisplayId(), hideSystemBar);
}
};
mApplicationContext.registerReceiverForAllUsers(immersiveModeChangeReceiver,
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaUtils.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaUtils.java
deleted file mode 100644
index ca4f27d..0000000
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/CarDisplayAreaUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.displayarea;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.car.app.CarActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-/**
- * Utils for CarDisplayArea package.
- */
-public final class CarDisplayAreaUtils {
-
- private static final String TAG = "CarDisplayAreaUtils";
-
- private CarDisplayAreaUtils() {
- }
-
- static boolean isCustomDisplayPolicyDefined(Context context) {
- Resources resources = context.getResources();
- String customPolicyName = null;
- try {
- customPolicyName = resources
- .getString(
- com.android.internal
- .R.string.config_deviceSpecificDisplayAreaPolicyProvider);
- } catch (Resources.NotFoundException ex) {
- Log.w(TAG, "custom policy provider not defined");
- }
- return customPolicyName != null && !customPolicyName.isEmpty();
- }
-
- static void setPersistentActivity(CarActivityManager am,
- ComponentName activity, int featureId, String featureName) {
- if (activity == null) {
- Log.e(TAG, "Empty activity for " + featureName + " (" + featureId + ")");
- return;
- }
- int ret = am.setPersistentActivity(activity, DEFAULT_DISPLAY, featureId);
- if (ret != CarActivityManager.RESULT_SUCCESS) {
- Log.e(TAG, "Failed to set PersistentActivity: activity=" + activity
- + ", ret=" + ret);
- }
- }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/DisplayAreaComponent.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/DisplayAreaComponent.java
index 9fe8d93..e23e450 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/DisplayAreaComponent.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/displayarea/DisplayAreaComponent.java
@@ -132,9 +132,7 @@
}
logIfDebuggable("start:");
- if (CarDisplayAreaUtils.isCustomDisplayPolicyDefined(mContext)) {
- mCarDisplayAreaController.register();
- doBindService();
- }
+ mCarDisplayAreaController.register();
+ doBindService();
}
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/AutoDismissHvacPanelOverlayViewController.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/AutoDismissHvacPanelOverlayViewController.java
index 246f6be..4326e55 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/AutoDismissHvacPanelOverlayViewController.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/AutoDismissHvacPanelOverlayViewController.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.car.CarDeviceProvisionedController;
@@ -75,6 +77,15 @@
}
@Override
+ protected void onTouchEvent(View view, MotionEvent event) {
+ int hvacHeight = mResources.getDimensionPixelSize(R.dimen.hvac_panel_full_expanded_height);
+ if (isPanelExpanded() && (event.getY() < (view.getHeight() - hvacHeight))
+ && (event.getAction() == MotionEvent.ACTION_UP)) {
+ mHandler.post(mAutoDismiss);
+ }
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/CarUiPortraitTemperatureControlView.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/CarUiPortraitTemperatureControlView.java
index f0c411f..b1b3bcc 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/CarUiPortraitTemperatureControlView.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/hvac/CarUiPortraitTemperatureControlView.java
@@ -155,8 +155,15 @@
*/
protected void updateTemperatureViewUiThread() {
mTempTextView.setText(mTempInDisplay);
- mTempTextView.setTextColor(mPowerOn && mTemperatureSetAvailable
- ? mAvailableTextColor : mUnavailableTextColor);
+ if (mPowerOn && mTemperatureSetAvailable) {
+ mTempTextView.setTextColor(mAvailableTextColor);
+ mIncreaseButton.setVisibility(View.VISIBLE);
+ mDecreaseButton.setVisibility(View.VISIBLE);
+ } else {
+ mTempTextView.setTextColor(mUnavailableTextColor);
+ mIncreaseButton.setVisibility(View.INVISIBLE);
+ mDecreaseButton.setVisibility(View.INVISIBLE);
+ }
}
protected String getTempInDisplay() {
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/qc/CarUiPortraitProfileSwitcher.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/qc/CarUiPortraitProfileSwitcher.java
index 9a60fc2..7a590bc 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/qc/CarUiPortraitProfileSwitcher.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/qc/CarUiPortraitProfileSwitcher.java
@@ -22,11 +22,13 @@
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.os.Handler;
import com.android.car.qc.QCItem;
import com.android.car.qc.QCRow;
import com.android.systemui.R;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -38,8 +40,8 @@
@Inject
public CarUiPortraitProfileSwitcher(Context context, UserTracker userTracker,
- CarServiceProvider carServiceProvider) {
- super(context, userTracker, carServiceProvider);
+ CarServiceProvider carServiceProvider, @Background Handler handler) {
+ super(context, userTracker, carServiceProvider, handler);
}
@@ -51,6 +53,15 @@
return super.createUserProfileRow(userInfo);
}
+ @Override
+ protected QCRow createGuestProfileRow() {
+ if (mUserTracker.getUserInfo() != null && mUserTracker.getUserInfo().isGuest()) {
+ return createGuestProfileRowForCurrentProfile();
+ } else {
+ return super.createGuestProfileRow();
+ }
+ }
+
private QCRow createUserProfileRowForCurrentProfile(UserInfo userInfo) {
QCItem.ActionHandler actionHandler = (item, context, intent) -> {
if (mPendingUserAdd) {
@@ -62,6 +73,23 @@
mUserIconProvider.getDrawableWithBadge(mContext, userInfo), actionHandler);
}
+ private QCRow createGuestProfileRowForCurrentProfile() {
+ QCItem.ActionHandler actionHandler = (item, context, intent) -> {
+ if (mPendingUserAdd) {
+ return;
+ }
+ UserInfo guest = createNewOrFindExistingGuest(mContext);
+ if (guest != null) {
+ switchUser(guest.id);
+ }
+ };
+
+ return createUserProfileRowForCurrentProfile(
+ mContext.getString(com.android.internal.R.string.guest_name),
+ mUserIconProvider.getRoundedGuestDefaultIcon(mContext),
+ actionHandler);
+ }
+
private QCRow createUserProfileRowForCurrentProfile(String title, Drawable iconDrawable,
QCItem.ActionHandler actionHandler) {
Icon icon = Icon.createWithBitmap(drawableToBitmap(iconDrawable));
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/statusicon/ui/StatusBarSensorInfoManager.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/statusicon/ui/StatusBarSensorInfoManager.java
index 7a7d8da..05a20ae 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/statusicon/ui/StatusBarSensorInfoManager.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/statusicon/ui/StatusBarSensorInfoManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.statusicon.ui;
+import static android.car.VehicleAreaSeat.SEAT_UNKNOWN;
import static android.car.VehiclePropertyIds.ENV_OUTSIDE_TEMPERATURE;
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
@@ -77,8 +78,8 @@
mSensorAvailabilityData.postValue(true);
mCarPropertyManager =
(CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
+ initializeHvacProperties();
registerHvacPropertyEventListeners();
-
});
} catch (Exception e) {
Log.e(TAG, "Failed to connect to Vhal", e);
@@ -102,6 +103,18 @@
R.string.statusbar_temperature_format_fahrenheit);
}
+ private void initializeHvacProperties() {
+ int temperatureAreaId =
+ mCarPropertyManager.getAreaId(ENV_OUTSIDE_TEMPERATURE, SEAT_UNKNOWN);
+ mTemperatureValueInCelsius =
+ mCarPropertyManager.getFloatProperty(ENV_OUTSIDE_TEMPERATURE, temperatureAreaId);
+ int unitAreaId =
+ mCarPropertyManager.getAreaId(HVAC_TEMPERATURE_DISPLAY_UNITS, SEAT_UNKNOWN);
+ mTemperatureUnit =
+ mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, unitAreaId);
+ updateHvacProperties();
+ }
+
private void registerHvacPropertyEventListeners() {
for (Integer propertyId : SENSOR_PROPERTIES) {
mCarPropertyManager.registerCallback(mPropertyEventCallback, propertyId,
@@ -124,7 +137,10 @@
return;
}
}
+ updateHvacProperties();
+ }
+ private void updateHvacProperties() {
boolean displayInInFahrenheit = mTemperatureUnit == VehicleUnit.FAHRENHEIT;
float tempToDisplay = displayInInFahrenheit ? celsiusToFahrenheit(
mTemperatureValueInCelsius)
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitAppGridButton.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitAppGridButton.java
index 438f156..887de41 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitAppGridButton.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitAppGridButton.java
@@ -17,13 +17,64 @@
package com.android.systemui.car.systembar;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
-/** The button used to show the app grid in the system bar. */
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+
+/** The button used to show the app grid and Recents in the system bar. */
public class CarUiPortraitAppGridButton extends CarUiPortraitSystemBarButton {
+ private RecentsButtonStateProvider mRecentsButtonStateProvider;
+ private boolean mIsAppGridActive;
public CarUiPortraitAppGridButton(Context context,
AttributeSet attrs) {
super(context, attrs);
}
+
+ @Override
+ protected void init() {
+ mRecentsButtonStateProvider = new CarUiRecentsButtonStateProvider(getContext(), this);
+ }
+
+ /**
+ * To set if the AppGrid activity is in foreground.
+ * The button selected state depends on AppGrid or Recents being in foreground.
+ */
+ public void setAppGridSelected(boolean selected) {
+ mIsAppGridActive = selected;
+ super.setSelected(mRecentsButtonStateProvider.getIsRecentsActive() || mIsAppGridActive);
+ }
+
+ /**
+ * To set if the Recents activity is in foreground.
+ * The button selected state depends on AppGrid or Recents being in foreground.
+ */
+ public void setRecentsSelected(boolean selected) {
+ mRecentsButtonStateProvider.setIsRecentsActive(selected);
+
+ super.setSelected(mRecentsButtonStateProvider.getIsRecentsActive() || mIsAppGridActive);
+ }
+
+ @Override
+ protected void setUpIntents(TypedArray typedArray) {
+ mRecentsButtonStateProvider.setUpIntents(typedArray, super::setUpIntents);
+ }
+
+ @Override
+ protected OnClickListener getButtonClickListener(Intent toSend) {
+ return mRecentsButtonStateProvider.getButtonClickListener(toSend,
+ super::getButtonClickListener);
+ }
+
+ @Override
+ protected void updateImage(AlphaOptimizedImageView icon) {
+ mRecentsButtonStateProvider.updateImage(icon, super::updateImage);
+ }
+
+ @Override
+ protected void refreshIconAlpha(AlphaOptimizedImageView icon) {
+ mRecentsButtonStateProvider.refreshIconAlpha(icon, super::refreshIconAlpha);
+ }
}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitButtonSelectionStateController.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitButtonSelectionStateController.java
index c837a45..7000175 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitButtonSelectionStateController.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiPortraitButtonSelectionStateController.java
@@ -61,13 +61,15 @@
/** Updates the selected state (for AppGrid activity) of the app grid button */
void setAppGridButtonSelected(boolean isSelected) {
if (mAppGridButton != null) {
- mAppGridButton.setSelected(isSelected);
+ mAppGridButton.setAppGridSelected(isSelected);
}
}
/** Updates the selected state (for Recents activity) of the app grid button */
void setRecentsButtonSelected(boolean isSelected) {
- // no-op
+ if (mAppGridButton != null) {
+ mAppGridButton.setRecentsSelected(isSelected);
+ }
}
/** Updates the selected state of the notification button */
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiRecentsButtonStateProvider.java b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiRecentsButtonStateProvider.java
index ca933e3..46e5e73 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiRecentsButtonStateProvider.java
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/src/com/android/systemui/car/systembar/CarUiRecentsButtonStateProvider.java
@@ -32,7 +32,7 @@
public CarUiRecentsButtonStateProvider(Context context, CarSystemBarButton carSystemBarButton) {
super(context, carSystemBarButton);
- mContext = carSystemBarButton.getContext();
+ mContext = context;
}
@Override
@@ -42,14 +42,14 @@
}
@Override
- protected void toggleRecents() {
+ protected boolean toggleRecents() {
if (getIsRecentsActive()) {
Intent intent = new Intent(REQUEST_FROM_SYSTEM_UI);
intent.putExtra(INTENT_EXTRA_COLLAPSE_RECENTS_PANEL, true);
mContext.getApplicationContext().sendBroadcastAsUser(intent,
new UserHandle(ActivityManager.getCurrentUser()));
- return;
+ return true;
}
- super.toggleRecents();
+ return super.toggleRecents();
}
}
diff --git a/car_product/car_ui_portrait/apps/car_ui_portrait_apps.mk b/car_product/car_ui_portrait/apps/car_ui_portrait_apps.mk
index 24beffa..51cf6fc 100644
--- a/car_product/car_ui_portrait/apps/car_ui_portrait_apps.mk
+++ b/car_product/car_ui_portrait/apps/car_ui_portrait_apps.mk
@@ -18,7 +18,6 @@
PRODUCT_PACKAGES += \
car-ui-lib-diagnostic-plugin \
car-ui-lib-portrait-proxyplugin \
- car-ui-lib-portrait-sharedlibrary \
CarNotification \
CarUiPortraitCommon \
CarUiPortraitLauncher \
diff --git a/car_product/car_ui_portrait/android.software.car.splitscreen_multitasking.xml b/car_product/car_ui_portrait/car_ui_portrait_hardware.xml
similarity index 84%
rename from car_product/car_ui_portrait/android.software.car.splitscreen_multitasking.xml
rename to car_product/car_ui_portrait/car_ui_portrait_hardware.xml
index 3c8770c..1438a43 100644
--- a/car_product/car_ui_portrait/android.software.car.splitscreen_multitasking.xml
+++ b/car_product/car_ui_portrait/car_ui_portrait_hardware.xml
@@ -15,11 +15,13 @@
~ limitations under the License.
-->
-<!-- With this feature, the device can display multiple tasks at the same time on a single display,
+<permissions>
+ <!-- With this feature, the device can display multiple tasks at the same time on a single display,
and the user can perform multiple actions on different tasks simultaneously. Apps open in split
screen mode by default, instead of full screen. The device decides how to show apps. Apps open
in split screen mode by default, instead of full screen. Unlike Android's multi-window mode,
where users can choose how to display apps, the device determines how apps are shown.-->
-<permissions>
<feature name="android.software.car.splitscreen_multitasking"/>
-</permissions>
\ No newline at end of file
+ <!-- Disallow third-party IME apps. -->
+ <unavailable-feature name="android.software.input_methods"/>
+</permissions>
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/AndroidManifest.xml
index 538f066..d849061 100644
--- a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarEvsCameraPreviewApp"
android:targetPackage="com.google.android.car.evs"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.carevs"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable-port/close_bg.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable/close_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable-port/close_bg.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable/close_bg.xml
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable-port/ic_close.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable/ic_close.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable-port/ic_close.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/drawable/ic_close.xml
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/layout-port/evs_preview_activity.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/layout/evs_preview_activity.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/layout-port/evs_preview_activity.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/layout/evs_preview_activity.xml
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/config.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/config.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/config.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/config.xml
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/strings.xml b/car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/strings.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values-port/strings.xml
rename to car_product/car_ui_portrait/rro/CarEvsCameraPreviewAppRRO/res/values/strings.xml
diff --git a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port-night/color_palettes.xml b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-night/color_palettes.xml
similarity index 98%
rename from car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port-night/color_palettes.xml
rename to car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-night/color_palettes.xml
index 1084e70..04322ea 100644
--- a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port-night/color_palettes.xml
+++ b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-night/color_palettes.xml
@@ -125,4 +125,5 @@
<!-- control focus colors -->
<color name="car_focus_background">#99b0c5ff</color>
+ <color name="car_panel_border">#4D5057</color>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port/color_palettes.xml b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/color_palettes.xml
similarity index 98%
rename from car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port/color_palettes.xml
rename to car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/color_palettes.xml
index e9a0f53..90475d2 100644
--- a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values-port/color_palettes.xml
+++ b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/color_palettes.xml
@@ -126,4 +126,5 @@
<color name="car_focus_ring">@color/car_primary</color>
<color name="car_focus_background">#990856cf</color>
+ <color name="car_panel_border">#ffffff</color>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/dimens.xml b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/dimens.xml
index 9e91b71..ac5ee86 100644
--- a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/dimens.xml
+++ b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/dimens.xml
@@ -19,7 +19,8 @@
<dimen name="car_portrait_ui_button_radius">44dp</dimen>
<dimen name="car_portrait_ui_window_rounded_corner_radius">48dp</dimen>
- <dimen name="car_portrait_ui_toast_margin">24dp</dimen>
+ <dimen name="car_portrait_ui_toast_margin">48dp</dimen>
+ <dimen name="car_portrait_ui_toast_icon_margin">24dp</dimen>
<dimen name="car_portrait_ui_toast_icon_size">48dp</dimen>
<dimen name="car_portrait_ui_toast_bottom_margin">32dp</dimen>
@@ -27,6 +28,9 @@
<dimen name="car_portrait_ui_tab_height">96dp</dimen>
<dimen name="car_portrait_ui_tab_margin_horizontal">16dp</dimen>
- <dimen name="car_alert_dialog_vertical_margin">32dp</dimen>
- <dimen name="car_alert_dialog_horizontal_margin">48dp</dimen>
+ <dimen name="car_alert_dialog_padding">24dp</dimen>
+ <dimen name="car_alert_dialog_icon_size">64dp</dimen>
+ <dimen name="car_alert_dialog_button_spacing">16dp</dimen>
+
+ <dimen name="car_panel_border_width">3dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/styles.xml b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/styles.xml
index a7b5242..622f977 100644
--- a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/res/values/styles.xml
@@ -22,7 +22,7 @@
<style name="TextAppearance.Car.Display">
- <item name="android:fontFamily">groboto-regular</item>
+ <item name="android:fontFamily">roboto-regular</item>
<item name="android:textFontWeight">500</item>
<item name="android:textSize">72sp</item>
</style>
@@ -115,7 +115,6 @@
<item name="android:lineHeight">40px</item>
<item name="android:textFontWeight">400</item>
<item name="android:textColor">@color/car_inverse_on_surface</item>
- <item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/src/com/android/car/caruiportrait/common/service/CarUiPortraitService.java b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/src/com/android/car/caruiportrait/common/service/CarUiPortraitService.java
index 8f57d2a..020b49a 100644
--- a/car_product/car_ui_portrait/rro/CarUIPortraitCommon/src/com/android/car/caruiportrait/common/service/CarUiPortraitService.java
+++ b/car_product/car_ui_portrait/rro/CarUIPortraitCommon/src/com/android/car/caruiportrait/common/service/CarUiPortraitService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -45,6 +46,11 @@
public static final String INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED =
"INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED";
+ // key name for the intent's extra that tells the component that request the view's
+ // visibility change.
+ public static final String INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE =
+ "INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE";
+
public static final String INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE =
"INTENT_EXTRA_IS_IMMERSIVE_MODE_STATE";
@@ -232,7 +238,11 @@
if (intent.hasExtra(INTENT_EXTRA_IS_IMMERSIVE_MODE_REQUESTED)
&& isImmersive != mIsSystemInImmersiveMode) {
mIsSystemInImmersiveMode = isImmersive;
- notifyClients(MSG_IMMERSIVE_MODE_REQUESTED, boolToInt(isImmersive));
+ String source = intent.getStringExtra(
+ INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE);
+ Bundle bundle = new Bundle();
+ bundle.putString(INTENT_EXTRA_IMMERSIVE_MODE_REQUESTED_SOURCE, source);
+ notifyClients(MSG_IMMERSIVE_MODE_REQUESTED, boolToInt(isImmersive), bundle);
}
boolean isImmersiveState = intent.getBooleanExtra(
@@ -282,6 +292,21 @@
} catch (RemoteException e) {
// The client is dead. Remove it from the list.
mClients.remove(i);
+ Log.d(TAG, "A client is removed from the list");
+ }
+ }
+ }
+
+ private void notifyClients(int key, int value, Bundle bundle) {
+ for (int i = mClients.size() - 1; i >= 0; i--) {
+ try {
+ Message msg = Message.obtain(null, key, value, 0);
+ msg.setData(bundle);
+ mClients.get(i).send(msg);
+ } catch (RemoteException e) {
+ // The client is dead. Remove it from the list.
+ mClients.remove(i);
+ Log.d(TAG, "A client is removed from the list");
}
}
}
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/AndroidManifest.xml
index 7f4fbad..77e6de6 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarAppsCommon"
android:targetPackage="com.android.car.portraitlauncher"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.carappscommon"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/chevron_down_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/chevron_down_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/chevron_down_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/chevron_down_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/chevron_up_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/chevron_up_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/chevron_up_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/chevron_up_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/ic_overflow_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/ic_overflow_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable-port/ic_overflow_button.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/drawable/ic_overflow_button.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/bools.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/integers.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/integers.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/integers.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/integers.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/styles.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitAppsCommonLauncherRRO/res/values/styles.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/AndroidManifest.xml
deleted file mode 100644
index 337cb82..0000000
--- a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/AndroidManifest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="will.be.replaced">
- <application android:hasCode="false"/>
- <!-- priority should be greater than car-ui-customization -->
- <overlay android:priority="11"
- android:targetName="car-qc-lib"
- android:targetPackage="will.be.replaced.too"
- android:resourcesMap="@xml/overlays"
- android:isStatic="false"/>
-</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/values-port/colors.xml
deleted file mode 100644
index c932d50..0000000
--- a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/values-port/colors.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-
-<resources>
- <!-- Quick Controls -->
- <!-- This is the actual qc panel icon color, ignore qc_default_icon_color -->
- <color name="qc_start_icon_color">@color/car_on_surface</color>
-</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitCarServiceRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitCarServiceRRO/AndroidManifest.xml
index 0c2e5ba..f435d85 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitCarServiceRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitCarServiceRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarServiceCustomization"
android:targetPackage="com.android.car.updatable"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.car"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/AndroidManifest.xml
index fb99fe0..efc9547 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarDialerApp"
android:targetPackage="com.android.car.dialer"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.cardialer"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/color-port/contact_details_icon_tint.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/color/contact_details_icon_tint.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/color-port/contact_details_icon_tint.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/color/contact_details_icon_tint.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/add_fav_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/add_fav_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/add_fav_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/add_fav_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/car_ui_portrait_ic_profile.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/car_ui_portrait_ic_profile.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/car_ui_portrait_ic_profile.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/car_ui_portrait_ic_profile.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/dialer_call_button_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_call_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/dialer_call_button_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_call_button_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/dialer_ripple_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_ripple_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/dialer_ripple_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_ripple_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_add.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_add.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_add.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_add.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_answer_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_answer_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_answer_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_answer_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_arrow_right.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_arrow_right.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_arrow_right.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_arrow_right.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_backspace.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_backspace.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_backspace.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_backspace.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_bluetooth.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_bluetooth.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_bluetooth.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_bluetooth.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_decline_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_decline_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_decline_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_decline_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_direction.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_direction.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_direction.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_direction.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite_activatable.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite_activatable.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite_activatable.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite_activatable.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite_empty.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite_empty.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_favorite_empty.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_favorite_empty.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_imported_favorite.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_imported_favorite.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_imported_favorite.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_imported_favorite.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_phone.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_phone.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_place.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_place.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_place.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_place.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_sms.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_sms.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/ic_sms.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_sms.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/keypad_default_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/keypad_default_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/restricted_dialing_mode_label_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/restricted_dialing_mode_label_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable-port/restricted_dialing_mode_label_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/restricted_dialing_mode_label_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/add_favorite_list_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/add_favorite_list_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/add_favorite_list_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/add_favorite_list_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_details_address.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_details_address.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_details_address.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_details_address.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_details_number.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_details_number.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_details_number.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_details_number.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_user_profile.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_user_profile.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/contact_user_profile.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/contact_user_profile.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/dialpad_fragment_with_type_down.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_fragment_with_type_down.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/dialpad_fragment_with_type_down.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_fragment_with_type_down.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/dialpad_user_profile.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_user_profile.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/dialpad_user_profile.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_user_profile.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/incall_dialpad_fragment.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/incall_dialpad_fragment.xml
similarity index 91%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/incall_dialpad_fragment.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/incall_dialpad_fragment.xml
index f6b93e1..981f16d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/incall_dialpad_fragment.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/incall_dialpad_fragment.xml
@@ -27,7 +27,8 @@
android:singleLine="true"
android:layout_marginTop="@dimen/in_call_title_margin"
android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true" />
+ android:layout_alignParentTop="true"
+ style="@style/TextAppearance.DialNumber" />
<Chronometer
android:id="@+id/call_state"
@@ -37,7 +38,8 @@
android:singleLine="true"
android:layout_marginTop="@dimen/in_call_state_margin"
android:layout_centerHorizontal="true"
- android:layout_below="@id/title" />
+ android:layout_below="@id/title"
+ style="@style/TextAppearance.InCallState" />
<com.android.car.ui.FocusArea
android:id="@+id/dialpad_focus_area"
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/no_hfp.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/no_hfp.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/no_hfp.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/no_hfp.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/ongoing_call_fragment.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/ongoing_call_fragment.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/ongoing_call_fragment.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/ongoing_call_fragment.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/restricted_dialing_mode_label.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/restricted_dialing_mode_label.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/restricted_dialing_mode_label.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/restricted_dialing_mode_label.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/type_down_list_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/type_down_list_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/type_down_list_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/type_down_list_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/user_profile_large.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/user_profile_large.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout-port/user_profile_large.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/user_profile_large.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/arrays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/arrays.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/arrays.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/arrays.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/configs.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/configs.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/configs.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/configs.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/integer.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/integer.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/integer.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/integer.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/strings.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/strings.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/strings.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/strings.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
similarity index 97%
rename from car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
index 1fb95db..c4cd711 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
@@ -135,4 +135,8 @@
<style name="TextAppearance.ContactDetailsListSubtitle" parent="TextAppearance.Car.Body.Small" >
<item name="android:textColor">@color/car_on_surface_variant</item>
</style>
+
+ <style name="TextAppearance.InCallState" parent="TextAppearance.Car.Body.Small" >
+ <item name="android:textColor">@color/car_on_surface_variant</item>
+ </style>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/AndroidManifest.xml
index f88bff7..9dbf6de 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarLauncher"
android:targetPackage="com.android.car.portraitlauncher"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.carlauncherref"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color-port/app_item_on_hover_background_color.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color/app_item_on_hover_background_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color-port/app_item_on_hover_background_color.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color/app_item_on_hover_background_color.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color-port/page_indicator_bar_color.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color/page_indicator_bar_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color-port/page_indicator_bar_color.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/color/page_indicator_bar_color.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/app_icon_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/app_icon_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/app_icon_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/app_icon_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/app_item_highlight.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/app_item_highlight.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/app_item_highlight.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/app_item_highlight.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/control_bar_contact_image_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/control_bar_contact_image_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/control_bar_contact_image_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/control_bar_contact_image_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/control_bar_image_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/control_bar_image_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/control_bar_image_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/control_bar_image_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/default_audio_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/default_audio_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/default_audio_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/default_audio_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/default_audio_background_gradient.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/default_audio_background_gradient.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/default_audio_background_gradient.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/default_audio_background_gradient.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_call_end.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_call_end.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_call_end.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_call_end.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_call_end_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_call_end_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_call_end_button.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_call_end_button.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_play_music.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_play_music.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/ic_play_music.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/ic_play_music.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/page_indicator_bar.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/page_indicator_bar.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/page_indicator_bar.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/page_indicator_bar.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/seekbar_thumb.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/seekbar_thumb.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable-port/seekbar_thumb.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/drawable/seekbar_thumb.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/app_grid_activity.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/app_grid_activity.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/app_grid_activity.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/app_grid_activity.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/app_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/app_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/app_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/app_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/car_launcher.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/car_launcher.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/car_launcher.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/car_launcher.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_descriptive_text_only.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_descriptive_text_only.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_descriptive_text_only.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_descriptive_text_only.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_descriptive_text_with_controls.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_descriptive_text_with_controls.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_descriptive_text_with_controls.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_descriptive_text_with_controls.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_media.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_media.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_media.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_media.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_text_block.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_text_block.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_content_text_block.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_content_text_block.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_fragment.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_fragment.xml
similarity index 98%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_fragment.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_fragment.xml
index 8f7cea6..4062233 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/card_fragment.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/card_fragment.xml
@@ -39,6 +39,7 @@
android:layout_height="@dimen/control_bar_image_size"
android:background="@drawable/control_bar_image_background"
android:layout_marginTop="@dimen/control_bar_image_container_margin"
+ android:layout_marginStart="@dimen/control_bar_image_container_margin"
android:layout_alignParentStart="true"
android:layout_alignBottom="@id/content_container">
<com.android.car.apps.common.CrossfadeImageView
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/descriptive_text.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/descriptive_text.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/descriptive_text.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/descriptive_text.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/optional_seek_bar_with_times.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/optional_seek_bar_with_times.xml
similarity index 84%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/optional_seek_bar_with_times.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/optional_seek_bar_with_times.xml
index 0378e2c..8abfc4c 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout-port/optional_seek_bar_with_times.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/layout/optional_seek_bar_with_times.xml
@@ -31,9 +31,8 @@
android:layout_height="match_parent"
android:layout_width="0dp"
android:orientation="vertical"
- android:layout_weight="0.6"
- android:gravity="center_vertical | start"
- android:layout_marginEnd="@dimen/seekbar_margin_end">
+ android:layout_weight="1"
+ android:gravity="center_vertical|start">
<SeekBar
android:id="@+id/optional_seek_bar"
android:layout_width="match_parent"
@@ -51,19 +50,21 @@
android:id="@+id/optional_progress_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
- style="@android:style/Widget.ProgressBar.Horizontal"
android:splitTrack="false"
android:progressTint="@color/seek_bar_color"
android:progressBackgroundTint="@color/seek_bar_background_tint_color"
android:paddingStart="@dimen/seekbar_padding"
android:paddingEnd="@dimen/seekbar_padding"
- android:max="@integer/optional_seekbar_max"/>
+ android:max="@integer/optional_seekbar_max"
+ style="@android:style/Widget.ProgressBar.Horizontal"/>
</LinearLayout>
<TextView
android:id="@+id/optional_times"
- android:layout_width="@dimen/seekbar_times_width"
- android:layout_height="match_parent"
- style="@style/TextAppearance.Car.Sub.Medium"
- android:gravity="center_vertical | end"
- android:textColor="@color/car_on_surface_variant"/>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/seekbar_times_width"
+ android:layout_marginStart="@dimen/seekbar_margin_end"
+ android:gravity="center_vertical|end"
+ android:textColor="@color/car_on_surface_variant"
+ style="@style/TextAppearance.Car.Sub.Medium"/>
</LinearLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-night/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-night/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/config.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/config.xml
deleted file mode 100644
index 0d391c82..0000000
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
- ~ 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.
- -->
-
-<resources>
- <!-- A list of package names that provide the cards to display on the home screen -->
- <string-array name="config_homeCardModuleClasses" translatable="false">
- <item>com.android.car.portraitlauncher.homescreen.audio.PortraitAudioCard</item>
- </string-array>
-</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/bools.xml
similarity index 96%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/bools.xml
index 46811fe..c6ae438 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/bools.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/bools.xml
@@ -31,7 +31,7 @@
<!-- Whether the app grid should use @dimen/app_grid_width and @dimen/app_grid_height.
If this is set to true, app grid will fill the available space, If this is set to false, exact
defined dimensions will be used instead.-->
- <bool name="use_defined_app_grid_dimensions">false</bool>
+ <bool name="use_defined_app_grid_dimensions">true</bool>
<!-- Whether the app grid should use vertical pagination (up and down swipe) in app grid
instead of horizontal pagination (left and right)-->
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/dimens.xml
similarity index 94%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/dimens.xml
index 0c05b9b..809c449 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/dimens.xml
@@ -73,13 +73,13 @@
<dimen name="seekbar_padding">0dp</dimen>
<dimen name="seekbar_marginHorizontal">@dimen/control_bar_image_container_margin</dimen>
<dimen name="seekbar_marginVertical">8dp</dimen>
- <dimen name="seekbar_margin_end">44dp</dimen>
- <dimen name="seekbar_times_width">180dp</dimen>
+ <dimen name="seekbar_margin_end">24dp</dimen>
+ <dimen name="seekbar_times_width">140dp</dimen>
<dimen name="seekbar_with_times_height">28dp</dimen>
<dimen name="seekbar_with_times_margin_bottom">6dp</dimen>
<dimen name="seekbar_with_times_margin_top">30dp</dimen>
<dimen name="recent_task_icon_size">88dp</dimen>
- <dimen name="recent_task_width_first">874dp</dimen>
- <dimen name="recent_task_width">369dp</dimen>
+ <dimen name="recent_task_width_first">962dp</dimen>
+ <dimen name="recent_task_width">402dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/integers.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/integers.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/integers.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/integers.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/strings.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/strings.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/strings.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/strings.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/styles.xml
similarity index 96%
rename from car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/styles.xml
index 3c60b40..0d6d197 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/values/styles.xml
@@ -34,7 +34,7 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:paddingTop">32dp</item>
- <item name="android:paddingBottom">53dp</item>
+ <item name="android:paddingBottom">56dp</item>
<item name="android:scrollbars">none</item>
<item name="android:clipToPadding">false</item>
</style>
@@ -42,7 +42,6 @@
<style name="ClearAllRecentTasksButton" parent="@android:Widget.DeviceDefault.Button">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
- <item name="android:layout_marginBottom">53dp</item>
</style>
<style name="RecentTaskIcon">
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/xml/overlays.xml
index 2cb142b..cfd5c0d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherReferenceRRO/res/xml/overlays.xml
@@ -117,8 +117,6 @@
<item target="drawable/ic_call_end" value="@drawable/ic_call_end" />
<item target="drawable/ic_call_end_button" value="@drawable/ic_call_end_button" />
- <item target="array/config_homeCardModuleClasses" value="@array/config_homeCardModuleClasses" />
-
<item target="string/default_media_song_title" value="@string/default_media_song_title" />
<item target="string/times_separator" value="@string/times_separator" />
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/AndroidManifest.xml
index 464ee1c..c5ae6ba 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarMediaCommon"
android:targetPackage="com.android.car.portraitlauncher"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.launcher.mediacommon"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/circle_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/circle_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/circle_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/circle_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/circle_to_square_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/circle_to_square_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/circle_to_square_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/circle_to_square_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/disabled_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/disabled_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/disabled_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/disabled_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_pause.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_pause.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_pause.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_pause.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_play_arrow.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_play_arrow.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_play_arrow.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_play_arrow.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_play_arrow_off.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_play_arrow_off.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_play_arrow_off.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_play_arrow_off.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_skip_next.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_skip_next.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_skip_next.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_skip_next.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_skip_previous.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_skip_previous.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_skip_previous.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_skip_previous.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_stop.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_stop.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/ic_stop.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/ic_stop.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/pause_to_play_arrow.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/pause_to_play_arrow.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/pause_to_play_arrow.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/pause_to_play_arrow.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_arrow_to_pause.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_arrow_to_pause.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_arrow_to_pause.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_arrow_to_pause.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_arrow_to_stop.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_arrow_to_stop.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_arrow_to_stop.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_arrow_to_stop.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_pause_stop.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_pause_stop.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_pause_stop.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_pause_stop.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_pause_stop_animated_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_pause_stop_animated_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/play_pause_stop_animated_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/play_pause_stop_animated_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_next_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_next_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_next_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_next_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_next_off_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_next_off_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_next_off_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_next_off_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_previous_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_previous_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_previous_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_previous_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_previous_off_icon.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_previous_off_icon.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/skip_previous_off_icon.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/skip_previous_off_icon.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/square_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/square_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/square_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/square_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/square_to_circle_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/square_to_circle_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/square_to_circle_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/square_to_circle_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/stop_to_play_arrow.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/stop_to_play_arrow.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable-port/stop_to_play_arrow.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/drawable/stop_to_play_arrow.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/layout-port/play_pause_stop_button_layout.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/layout/play_pause_stop_button_layout.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/layout-port/play_pause_stop_button_layout.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/layout/play_pause_stop_button_layout.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/attrs.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/attrs.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/attrs.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/bools.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/styles.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonLauncherRRO/res/values/styles.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml
index 82dfa4d..b66e9d3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarMediaCommon"
android:targetPackage="com.android.car.media"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.media.common"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values-port/config.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values/config.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values-port/config.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values/config.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/AndroidManifest.xml
index 0dd1622..ed9bd15 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarMediaApp"
android:targetPackage="com.android.car.media"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.media"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/error_button_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/error_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/error_button_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/error_button_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/grid_image_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/grid_image_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/grid_image_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/grid_image_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_chevron_right.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_chevron_right.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_chevron_right.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_chevron_right.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_equalizer.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_equalizer.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_equalizer.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_equalizer.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_explicit.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_explicit.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_explicit.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_explicit.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_file_download_done.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_file_download_done.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/ic_file_download_done.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/ic_file_download_done.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/image_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/image_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable-port/image_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/drawable/image_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar.xml
new file mode 100644
index 0000000..b5f4061
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/MinimizedControlBar"
+ android:id="@+id/browse_mini_control_bar_layout"
+ android:layout_margin="@dimen/grid_item_spacing"
+ android:focusable="true"
+ android:gravity="center">
+
+ <RelativeLayout
+ android:id="@+id/browse_mini_control_bar_content_tile_container"
+ android:layout_width="@dimen/minimized_control_bar_content_tile_size"
+ android:layout_height="@dimen/minimized_control_bar_content_tile_size"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_alignParentTop="true">
+
+ <ImageView
+ android:id="@+id/browse_mini_control_bar_content_tile"
+ style="@style/MinimizedControlBarContentTileStyle"
+ android:layout_width="@dimen/minimized_control_bar_content_tile_size"
+ android:layout_height="@dimen/minimized_control_bar_content_tile_size"
+ android:scaleType="centerCrop"
+ />
+
+ <ImageView
+ android:id="@+id/browse_mini_control_bar_app_icon"
+ android:layout_width="@dimen/minimized_control_bar_app_icon_size"
+ android:layout_height="@dimen/minimized_control_bar_app_icon_size"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentBottom="true"
+ />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/browse_mini_control_bar_title"
+ style="@style/MinimizedControlBarTitleStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:includeFontPadding="false"
+ android:layout_marginTop="@dimen/media_browse_grid_item_text_margin_top"
+ android:layout_marginBottom="@dimen/media_browse_grid_item_margin_bottom"
+ android:padding="@dimen/media_browse_grid_item_padding"
+ android:layout_below="@id/browse_mini_control_bar_content_tile_container"
+ android:gravity="center"
+ />
+
+ <TextView
+ android:id="@+id/browse_mini_control_bar_subtitle"
+ style="@style/MinimizedControlBarSubtitleStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:includeFontPadding="false"
+ android:layout_marginHorizontal="@dimen/grid_item_spacing"
+ android:padding="@dimen/media_browse_grid_item_padding"
+ android:gravity="center"
+ />
+</RelativeLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_container.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_container.xml
new file mode 100644
index 0000000..e709628
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_container.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<com.android.car.ui.FrameFocusArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/container_browse_mini_item_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:paddingTop="@dimen/container_browse_mini_item_bar_padding_top">
+
+ <com.android.car.media.browse.BrowseMiniMediaItemView
+ android:id="@+id/browse_mini_item_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+</com.android.car.ui.FrameFocusArea>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_view.xml
similarity index 60%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_view.xml
index 8ad0860..d906c04 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/browse_mini_bar_view.xml
@@ -14,6 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <com.android.car.media.browse.BrowseMiniMediaItemBar
+ android:id="@+id/browse_mini_control_bar"
+ android:layout_width="@dimen/minimized_control_bar_width"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/fragment_error.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/fragment_error.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/fragment_error.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/fragment_error.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_grid_icons_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_grid_icons_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_grid_icons_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_grid_icons_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_grid_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_grid_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_grid_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_grid_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_header_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_header_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_header_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_header_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_list_icons_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_list_icons_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_list_icons_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_list_icons_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_list_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_list_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout-port/media_browse_list_item.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/layout/media_browse_list_item.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-night/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-night/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/attrs.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/attrs.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/attrs.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/bools.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/dimens.xml
similarity index 83%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/dimens.xml
index 69a7c6d..fb9888d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/dimens.xml
@@ -43,4 +43,11 @@
<dimen name="fragment_error_button_width">760dp</dimen>
<dimen name="fragment_error_button_height">88dp</dimen>
<dimen name="fragment_error_button_marginTop">120dp</dimen>
+
+ <!-- browse_mini_bar.xml, browse_mini_bar_container.xml & browse_mini_bar_view.xml -->
+ <dimen name="minimized_control_bar_app_icon_size">40dp</dimen>
+ <dimen name="minimized_control_bar_content_tile_size">280dp</dimen>
+ <dimen name="minimized_control_bar_width">@dimen/minimized_control_bar_content_tile_size</dimen>
+ <!-- Match car_ui_toolbar_row_height -->
+ <dimen name="container_browse_mini_item_bar_padding_top">96dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/strings.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/strings.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/strings.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/strings.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/styles.xml
similarity index 87%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/styles.xml
index 0d0a390..4e74d49 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/values/styles.xml
@@ -39,6 +39,13 @@
<style name="BrowseListItemRightArrowStyle">
<item name="android:src">@null</item>
</style>
+ <style name="MinimizedControlBarTitleStyle" parent="TextAppearance.Car.Body.Medium">
+ <item name="android:textColor">@color/car_on_surface</item>
+ </style>
+ <style name="MinimizedControlBarSubtitleStyle" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/car_on_surface_variant</item>
+ </style>
+ <style name="MinimizedControlBarContentTileStyle" parent="MediaGridIconContainerStyle"/>
<style name="BrowseSubheaderStyle" parent="TextAppearance.Car.Headline.Medium">
<item name="android:paddingHorizontal">8dp</item>
@@ -91,4 +98,9 @@
<item name="android:gravity">center_vertical</item>
<item name="android:textStyle">bold</item>
</style>
+
+ <style name="MinimizedControlBar">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/xml/overlays.xml
index ddbe669..57b9c63 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaRRO/res/xml/overlays.xml
@@ -48,54 +48,66 @@
<item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf"/>
<item target="attr/layout_constraintTop_toTopOf" value="@attr/layout_constraintTop_toTopOf"/>
- <item target="bool/show_mini_playback_controls" value="@bool/show_mini_playback_controls" />
- <item target="bool/show_persistent_tabs" value="@bool/show_persistent_tabs" />
- <item target="bool/switch_to_playback_view_when_playable_item_is_clicked" value="@bool/switch_to_playback_view_when_playable_item_is_clicked" />
+ <item target="bool/show_mini_playback_controls" value="@bool/show_mini_playback_controls"/>
+ <item target="bool/show_persistent_tabs" value="@bool/show_persistent_tabs"/>
+ <item target="bool/switch_to_playback_view_when_playable_item_is_clicked"
+ value="@bool/switch_to_playback_view_when_playable_item_is_clicked"/>
- <item target="id/download_icon_with_subtitle" value="@id/download_icon_with_subtitle" />
- <item target="id/download_icon_with_title" value="@id/download_icon_with_title" />
- <item target="id/error_button" value="@id/error_button" />
- <item target="id/error_message" value="@id/error_message" />
- <item target="id/explicit_icon_with_subtitle" value="@id/explicit_icon_with_subtitle" />
- <item target="id/explicit_icon_with_title" value="@id/explicit_icon_with_title" />
- <item target="id/focus_area" value="@id/focus_area" />
- <item target="id/item_container" value="@id/item_container" />
- <item target="id/menu_item_equalizer" value="@id/menu_item_equalizer" />
- <item target="id/menu_item_search" value="@id/menu_item_search" />
+ <item target="id/browse_mini_control_bar_layout" value="@id/browse_mini_control_bar_layout"/>
+ <item target="id/browse_mini_control_bar_content_tile" value="@id/browse_mini_control_bar_content_tile"/>
+ <item target="id/browse_mini_control_bar_app_icon" value="@id/browse_mini_control_bar_app_icon"/>
+ <item target="id/browse_mini_control_bar_title" value="@id/browse_mini_control_bar_title"/>
+ <item target="id/browse_mini_control_bar_subtitle" value="@id/browse_mini_control_bar_subtitle"/>
+ <item target="id/browse_mini_item_bar" value="@id/browse_mini_item_bar"/>
+ <item target="id/browse_mini_control_bar" value="@id/browse_mini_control_bar"/>
+ <item target="id/container_browse_mini_item_bar" value="@id/container_browse_mini_item_bar"/>
+ <item target="id/download_icon_with_subtitle" value="@id/download_icon_with_subtitle"/>
+ <item target="id/download_icon_with_title" value="@id/download_icon_with_title"/>
+ <item target="id/error_button" value="@id/error_button"/>
+ <item target="id/error_message" value="@id/error_message"/>
+ <item target="id/explicit_icon_with_subtitle" value="@id/explicit_icon_with_subtitle"/>
+ <item target="id/explicit_icon_with_title" value="@id/explicit_icon_with_title"/>
+ <item target="id/focus_area" value="@id/focus_area"/>
+ <item target="id/item_container" value="@id/item_container"/>
+ <item target="id/menu_item_equalizer" value="@id/menu_item_equalizer"/>
+ <item target="id/menu_item_search" value="@id/menu_item_search"/>
<item target="id/menu_item_selector" value="@id/menu_item_selector" />
<item target="id/menu_item_selector_with_source_logo" value="@id/menu_item_selector_with_source_logo" />
<item target="id/menu_item_setting" value="@id/menu_item_setting" />
- <item target="id/right_arrow" value="@id/right_arrow" />
- <item target="id/subtitle" value="@id/subtitle" />
- <item target="id/thumbnail" value="@id/thumbnail" />
- <item target="id/title" value="@id/title" />
- <item target="id/ui_content_bottom_guideline" value="@id/ui_content_bottom_guideline" />
- <item target="id/ui_content_end_guideline" value="@id/ui_content_end_guideline" />
- <item target="id/ui_content_start_guideline" value="@id/ui_content_start_guideline" />
- <item target="id/ui_content_top_guideline" value="@id/ui_content_top_guideline" />
+ <item target="id/right_arrow" value="@id/right_arrow"/>
+ <item target="id/subtitle" value="@id/subtitle"/>
+ <item target="id/thumbnail" value="@id/thumbnail"/>
+ <item target="id/title" value="@id/title"/>
+ <item target="id/ui_content_bottom_guideline" value="@id/ui_content_bottom_guideline"/>
+ <item target="id/ui_content_end_guideline" value="@id/ui_content_end_guideline"/>
+ <item target="id/ui_content_start_guideline" value="@id/ui_content_start_guideline"/>
+ <item target="id/ui_content_top_guideline" value="@id/ui_content_top_guideline"/>
<item target="layout/fragment_error" value="@layout/fragment_error"/>
+ <item target="layout/browse_mini_bar" value="@layout/browse_mini_bar"/>
+ <item target="layout/browse_mini_bar_container" value="@layout/browse_mini_bar_container"/>
+ <item target="layout/browse_mini_bar_view" value="@layout/browse_mini_bar_view"/>
<item target="layout/media_browse_grid_icons_item" value="@layout/media_browse_grid_icons_item"/>
<item target="layout/media_browse_grid_item" value="@layout/media_browse_grid_item"/>
<item target="layout/media_browse_header_item" value="@layout/media_browse_header_item"/>
<item target="layout/media_browse_list_icons_item" value="@layout/media_browse_list_icons_item"/>
<item target="layout/media_browse_list_item" value="@layout/media_browse_list_item"/>
- <item target="string/menu_item_app_selector_title" value="@string/menu_item_app_selector_title" />
- <item target="string/menu_item_sound_settings_title" value="@string/menu_item_sound_settings_title" />
+ <item target="string/menu_item_app_selector_title" value="@string/menu_item_app_selector_title"/>
+ <item target="string/menu_item_sound_settings_title" value="@string/menu_item_sound_settings_title"/>
- <item target="style/BrowseGridSubtitleStyle" value="@style/BrowseGridSubtitleStyle" />
- <item target="style/BrowseGridTitleStyle" value="@style/BrowseGridTitleStyle" />
- <item target="style/BrowseListItemRightArrowStyle" value="@style/BrowseListItemRightArrowStyle" />
- <item target="style/BrowseListSubtitleStyle" value="@style/BrowseListSubtitleStyle" />
- <item target="style/BrowseListTitleStyle" value="@style/BrowseListTitleStyle" />
- <item target="style/BrowseSubheaderStyle" value="@style/BrowseSubheaderStyle" />
- <item target="style/ErrorButtonStyle" value="@style/ErrorButtonStyle" />
- <item target="style/ErrorTextStyle" value="@style/ErrorTextStyle" />
- <item target="style/MediaIconContainerStyle" value="@style/MediaIconContainerStyle" />
- <item target="style/QueueListItemSubtitleStyle" value="@style/QueueListItemSubtitleStyle" />
- <item target="style/QueueListItemTimeStyle" value="@style/QueueListItemTimeStyle" />
- <item target="style/QueueListItemTitlesContainerStyle" value="@style/QueueListItemTitlesContainerStyle" />
- <item target="style/QueueListItemTitleStyle" value="@style/QueueListItemTitleStyle" />
- <item target="style/TextAppearance.NoContent" value="@style/TextAppearance.NoContent" />
+ <item target="style/BrowseGridSubtitleStyle" value="@style/BrowseGridSubtitleStyle"/>
+ <item target="style/BrowseGridTitleStyle" value="@style/BrowseGridTitleStyle"/>
+ <item target="style/BrowseListItemRightArrowStyle" value="@style/BrowseListItemRightArrowStyle"/>
+ <item target="style/BrowseListSubtitleStyle" value="@style/BrowseListSubtitleStyle"/>
+ <item target="style/BrowseListTitleStyle" value="@style/BrowseListTitleStyle"/>
+ <item target="style/BrowseSubheaderStyle" value="@style/BrowseSubheaderStyle"/>
+ <item target="style/ErrorButtonStyle" value="@style/ErrorButtonStyle"/>
+ <item target="style/ErrorTextStyle" value="@style/ErrorTextStyle"/>
+ <item target="style/MediaIconContainerStyle" value="@style/MediaIconContainerStyle"/>
+ <item target="style/QueueListItemSubtitleStyle" value="@style/QueueListItemSubtitleStyle"/>
+ <item target="style/QueueListItemTimeStyle" value="@style/QueueListItemTimeStyle"/>
+ <item target="style/QueueListItemTitlesContainerStyle" value="@style/QueueListItemTitlesContainerStyle"/>
+ <item target="style/QueueListItemTitleStyle" value="@style/QueueListItemTitleStyle"/>
+ <item target="style/TextAppearance.NoContent" value="@style/TextAppearance.NoContent"/>
</overlay>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/AndroidManifest.xml
index 4501326..7cd7028 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarMessengerApp"
android:targetPackage="com.android.car.messenger"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.carmessenger"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/drawable-port/ic_reply.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/drawable/ic_reply.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/drawable-port/ic_reply.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/drawable/ic_reply.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-night/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-night/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitMessengerRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/AndroidManifest.xml
index b1a8065..e3f6dee 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/AndroidManifest.xml
@@ -22,6 +22,7 @@
android:targetName="CarNotification"
android:targetPackage="com.android.car.notification"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.notifications"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_mute.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_mute.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_mute.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_mute.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_play_arrow.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_play_arrow.xml
similarity index 80%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_play_arrow.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_play_arrow.xml
index fb87a39..32d3d35 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_play_arrow.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_play_arrow.xml
@@ -16,11 +16,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:autoMirrored="true"
- android:viewportWidth="24"
- android:viewportHeight="24"
+ android:viewportWidth="37"
+ android:viewportHeight="36"
android:width="@dimen/play_arrow_width"
android:height="@dimen/play_arrow_height">
<path
android:fillColor="@color/play_icon_tint"
- android:pathData="M10,8.64L15.27,12 10,15.36V8.64M8,5v14l11,-7L8,5z"/>
+ android:pathData="M30.333,18L9.333,31.5V4.5L30.333,18ZM23.223,18L13.158,11.52V24.48L23.223,18Z"
+ android:fillType="evenOdd"/>
</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_reply.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_reply.xml
similarity index 76%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_reply.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_reply.xml
index f944a10..f880a8b 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_reply.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_reply.xml
@@ -16,11 +16,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:autoMirrored="true"
- android:viewportWidth="24"
- android:viewportHeight="24"
+ android:viewportWidth="36"
+ android:viewportHeight="36"
android:width="@dimen/reply_icon_width"
android:height="@dimen/reply_icon_height">
<path
android:fillColor="@color/icon_tint"
- android:pathData="M16,10H6.83L9,7.83l1.41,-1.41L9,5l-6,6 6,6 1.41,-1.41L9,14.17 6.83,12H16c1.65,0 3,1.35 3,3v4h2v-4c0,-2.76 -2.24,-5 -5,-5z"/>
+ android:pathData="M24,15H10.245L13.5,11.745L15.615,9.63L13.5,7.5L4.5,16.5L13.5,25.5L15.615,23.385L13.5,21.255L10.245,18H24C26.475,18 28.5,20.025 28.5,22.5V28.5H31.5V22.5C31.5,18.36 28.14,15 24,15Z"/>
</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_unmute.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_unmute.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/ic_unmute.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/ic_unmute.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/play_button_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/play_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable-port/play_button_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/drawable/play_button_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/basic_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/basic_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/basic_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/basic_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/call_headsup_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/call_headsup_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/call_headsup_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/call_headsup_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_emergency_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_emergency_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_emergency_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_emergency_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_headsup_notification_body_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_headsup_notification_body_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_headsup_notification_body_view.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_headsup_notification_body_view.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_headsup_notification_header_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_headsup_notification_header_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_headsup_notification_header_view.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_headsup_notification_header_view.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_information_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_information_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_information_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_information_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_action_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_action_button.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_action_button.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_action_button.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_actions_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_actions_view.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_body_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_body_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_body_view.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_body_view.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_header_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_header_view.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_notification_header_view.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_header_view.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_warning_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_warning_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/car_warning_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_warning_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/group_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/group_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/group_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/group_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/headsup_container_bottom.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/headsup_container_bottom.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/headsup_container_bottom.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/headsup_container_bottom.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/inbox_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/inbox_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/inbox_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/inbox_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/message_headsup_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/message_headsup_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/message_headsup_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/message_headsup_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/message_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/message_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/message_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/message_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_center_activity.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_center_activity.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_center_activity.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_center_activity.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_footer_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_footer_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_footer_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_footer_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_header_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_header_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/notification_header_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/notification_header_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/progress_notification_template.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/progress_notification_template.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout-port/progress_notification_template.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/progress_notification_template.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-car/styles.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-car/styles.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/themes.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-car/themes.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/themes.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-car/themes.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-night/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-night/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/attrs.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/attrs.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/attrs.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/config.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/config.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/config.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/config.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/dimens.xml
similarity index 97%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/dimens.xml
index b7aa3f5..f372104 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/dimens.xml
@@ -42,9 +42,9 @@
<dimen name="progress_bar_top_margin">8dp</dimen>
<dimen name="progress_bar_start_margin">24dp</dimen>
- <dimen name="play_arrow_width">30dp</dimen>
+ <dimen name="play_arrow_width">37dp</dimen>
<dimen name="play_arrow_height">36dp</dimen>
- <dimen name="reply_icon_height">30dp</dimen>
+ <dimen name="reply_icon_height">36dp</dimen>
<dimen name="reply_icon_width">36dp</dimen>
<dimen name="mute_icon_height">36dp</dimen>
<dimen name="mute_icon_width">36dp</dimen>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/strings.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/strings.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values-port/strings.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/values/strings.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/AndroidManifest.xml
index d38aee6..93350b9 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarRadio"
android:targetPackage="com.android.car.radio"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.radio"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/styles.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitRadioRRO/res/values/styles.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/AndroidManifest.xml
index deafc8b..55dac30 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="SettingsProvider"
android:targetPackage="com.android.providers.settings"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.settings.provider.emu"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/res/values-port/defaults.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/res/values/defaults.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/res/values-port/defaults.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderEmuRRO/res/values/defaults.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/AndroidManifest.xml
index 2814770..0c854a1 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="SettingsProvider"
android:targetPackage="com.android.providers.settings"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.settings.provider"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/res/values-port/defaults.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/res/values/defaults.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/res/values-port/defaults.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsProviderRRO/res/values/defaults.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/AndroidManifest.xml
index a05c53f..71afcb6 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/AndroidManifest.xml
@@ -21,6 +21,7 @@
android:targetName="CarSettings"
android:targetPackage="com.android.car.settings"
android:resourcesMap="@xml/overlays"
- android:category="caruiportrait.settings"
- android:isStatic="false" />
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true" />
</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator-port/trans_fade_in.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator/trans_fade_in.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator-port/trans_fade_in.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator/trans_fade_in.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator-port/trans_fade_out.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator/trans_fade_out.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator-port/trans_fade_out.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/animator/trans_fade_out.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background.xml
similarity index 79%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background.xml
index 42b5d04..d0baec3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
- android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
+ android:color="@color/car_secondary_container"/>
+ <item android:state_checked="false"
+ android:color="@color/car_secondary_container"/>
+ <item android:color="@color/car_primary"/>
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background_unavailable.xml
similarity index 77%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background_unavailable.xml
index 8ad0860..360cd7f 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_background_unavailable.xml
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_secondary_container"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground.xml
similarity index 79%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground.xml
index 42b5d04..e549e05 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
- android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
+ android:color="@color/car_on_secondary_container"/>
+ <item android:state_checked="false"
+ android:color="@color/car_on_secondary_container"/>
+ <item android:color="@color/car_on_primary"/>
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground_unavailable.xml
similarity index 77%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground_unavailable.xml
index 8ad0860..d84680d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/toggle_button_foreground_unavailable.xml
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_secondary_container"/>
+</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_icon_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_icon_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_icon_default.xml
similarity index 83%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_icon_default.xml
index 42b5d04..d19c149 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_icon_default.xml
@@ -14,10 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
android:color="@color/car_on_surface_variant"/>
<item android:state_activated="true" android:color="@color/car_on_secondary_container" />
+ <item app:state_ux_restricted="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_surface_variant"/>
<item android:color="@color/car_on_surface_variant" />
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_title.xml
similarity index 82%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_title.xml
index 42b5d04..d19c149 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color/top_level_title.xml
@@ -14,10 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
android:color="@color/car_on_surface_variant"/>
<item android:state_activated="true" android:color="@color/car_on_secondary_container" />
+ <item app:state_ux_restricted="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_surface_variant"/>
<item android:color="@color/car_on_surface_variant" />
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button.xml
new file mode 100644
index 0000000..e18d18d
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
+ <path
+ android:fillColor="@color/toggle_button_foreground"
+ android:pathData="M5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="@color/toggle_button_foreground"
+ android:pathData="M19,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button_unavailable.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button_unavailable.xml
new file mode 100644
index 0000000..493c06f
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_button_unavailable.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background_unavailable" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground_unavailable"
+ android:pathData="M13,5.83l1.88,1.88 -1.6,1.6 1.41,1.41 3.02,-3.02L12,2h-1v5.03l2,2v-3.2zM5.41,4L4,5.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l4.29,-4.29 2.3,2.29L20,18.59 5.41,4zM13,18.17v-3.76l1.88,1.88L13,18.17z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button.xml
new file mode 100644
index 0000000..0a0ff5f
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground"
+ android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button_unavailable.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button_unavailable.xml
new file mode 100644
index 0000000..e3d9c26
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_media_button_unavailable.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background_unavailable" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground_unavailable"
+ android:pathData="M21.19,21.19L14,14l-2,-2 -9.2,-9.2 -1.41,1.42 8.79,8.79c-0.06,0 -0.12,-0.01 -0.18,-0.01 -2.21,0 -4,1.79 -4,4s1.79,4 4.01,4S14,19.21 14,17v-0.17l5.78,5.78 1.41,-1.42zM14,11.17V7h4V3h-6v6.17z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button.xml
new file mode 100644
index 0000000..09f829e
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground"
+ android:pathData="M7.96,14.46l2.62,2.62c2.75,-1.49 5.01,-3.75 6.5,-6.5l-2.62,-2.62c-0.24,-0.24 -0.34,-0.58 -0.27,-0.9l0.65,-3.26c0.09,-0.46 0.5,-0.8 0.98,-0.8h4.15c0.56,0 1.03,0.47 1,1.03 -0.17,2.91 -1.04,5.63 -2.43,8.01 -1.57,2.69 -3.81,4.93 -6.5,6.5 -2.38,1.39 -5.1,2.26 -8.01,2.43 -0.56,0.03 -1.03,-0.44 -1.03,-1v-4.15c0,-0.48 0.34,-0.89 0.8,-0.98l3.26,-0.65c0.33,-0.07 0.67,0.04 0.9,0.27z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button_unavailable.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button_unavailable.xml
new file mode 100644
index 0000000..6dd12f1
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/bluetooth_phone_button_unavailable.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:width="@dimen/toggle_button_size"
+ android:height="@dimen/toggle_button_size"
+ android:drawable="@drawable/toggle_button_background_unavailable" />
+ <item
+ android:start="@dimen/toggle_button_icon_inset"
+ android:top="@dimen/toggle_button_icon_inset"
+ android:end="@dimen/toggle_button_icon_inset"
+ android:bottom="@dimen/toggle_button_icon_inset">
+ <vector
+ android:width="@dimen/icon_size"
+ android:height="@dimen/icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/toggle_button_foreground_unavailable"
+ android:pathData="M14.22,17.05c-0.69,0.55 -1.41,1.05 -2.18,1.49 -2.38,1.39 -5.1,2.26 -8.01,2.43 -0.56,0.03 -1.03,-0.44 -1.03,-1v-4.15c0,-0.48 0.34,-0.89 0.8,-0.98l3.26,-0.65c0.33,-0.07 0.67,0.04 0.9,0.27l2.62,2.62c0.78,-0.42 1.52,-0.91 2.22,-1.45L1.39,4.22l1.42,-1.41L21.19,21.2l-1.41,1.41 -5.56,-5.56zM15.62,12.82c0.55,-0.7 1.04,-1.45 1.47,-2.24l-2.62,-2.62c-0.24,-0.24 -0.34,-0.58 -0.27,-0.9l0.65,-3.26c0.09,-0.46 0.5,-0.8 0.98,-0.8h4.15c0.56,0 1.03,0.47 1,1.03 -0.17,2.91 -1.04,5.63 -2.43,8.01 -0.45,0.77 -0.96,1.51 -1.51,2.2l-1.42,-1.42z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background.xml
similarity index 63%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background.xml
index 42b5d04..de05d13 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background.xml
@@ -14,10 +14,17 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?android:attr/disabledAlpha"
- android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
+ <item android:state_checked="true" android:state_enabled="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/toggle_button_background" />
+ <corners android:radius="@dimen/toggle_button_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/toggle_button_background" />
+ </shape>
+ </item>
</selector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background_unavailable.xml
similarity index 79%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
copy to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background_unavailable.xml
index 36ee983..9343a4b 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port-night/colors.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/toggle_button_background_unavailable.xml
@@ -14,6 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_surface_variant</color>
-</resources>
+<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/toggle_button_background_unavailable"/>
+</shape>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable-port/top_level_preference_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/top_level_preference_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable-port/top_level_preference_background.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/top_level_preference_background.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable-port/top_level_preference_highlight.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/top_level_preference_highlight.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable-port/top_level_preference_highlight.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/drawable/top_level_preference_highlight.xml
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout-port/top_level_preference.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout/top_level_preference.xml
similarity index 97%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout-port/top_level_preference.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout/top_level_preference.xml
index 01b76d5..b8967e3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout-port/top_level_preference.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/layout/top_level_preference.xml
@@ -26,7 +26,7 @@
android:minHeight="@dimen/car_portrait_ui_tab_height"
android:tag="carUiPreference">
- <ImageView
+ <com.android.car.ui.uxr.DrawableStateImageView
android:id="@android:id/icon"
android:layout_width="44dp"
android:layout_height="44dp"
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/attrs.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/attrs.xml
new file mode 100644
index 0000000..6b60f12
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/attrs.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <attr name="state_ux_restricted" format="boolean" />
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/colors.xml
similarity index 89%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/colors.xml
index 8ad0860..839be1b 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/colors.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
+ <color name="qc_default_icon_color">@color/car_on_surface_variant</color>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/dimens.xml
similarity index 75%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/dimens.xml
index 3ce99da..8138d92 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/dimens.xml
@@ -15,6 +15,11 @@
~ limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <dimen name="icon_size">@*android:dimen/car_primary_icon_size</dimen>
<dimen name="top_level_menu_width">362dp</dimen>
<dimen name="car_portrait_ui_tab_margin_vertical">1dp</dimen>
+ <dimen name="toggle_button_size">72dp</dimen>
+ <dimen name="toggle_button_radius">16dp</dimen>
+ <dimen name="toggle_button_icon_inset">14dp</dimen>
+ <dimen name="toggle_button_icon_size">44dp</dimen>
</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/styles.xml
similarity index 85%
rename from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/styles.xml
index 361d33a..a2bd0eb 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values/styles.xml
@@ -24,11 +24,11 @@
<style name="CarSettingsFragmentContainerStyle">
<item name="android:layout_marginHorizontal">16dp</item>
</style>
- <style name="TopLevelPreferenceTitleStyle" parent="TextAppearance.CarUi.PreferenceTitle">
- <item name="android:textColor">@color/top_level_preference_text_color</item>
+ <style name="TopLevelPreferenceTitleStyle" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/top_level_title</item>
</style>
- <style name="TopLevelPreferenceSummaryStyle" parent="TextAppearance.CarUi.PreferenceSummary">
- <item name="android:textColor">@color/top_level_preference_text_color</item>
+ <style name="TopLevelPreferenceSummaryStyle" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/top_level_title</item>
</style>
<style name="CarSettingsActivityDividerStyle">
<item name="android:visibility">gone</item>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/xml/overlays.xml
index ad741d6..cfa8386 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/xml/overlays.xml
@@ -15,7 +15,18 @@
~ limitations under the License.
-->
<overlay>
+ <item target="animator/trans_left_in" value="@animator/trans_fade_in"/>
+ <item target="animator/trans_left_out" value="@animator/trans_fade_out"/>
+ <item target="animator/trans_right_in" value="@animator/trans_fade_in"/>
+ <item target="animator/trans_right_out" value="@animator/trans_fade_out"/>
+
+ <item target="attr/state_ux_restricted" value="@attr/state_ux_restricted"/>
+
+ <item target="bool/config_top_level_injection_background_always_use_default" value="true" />
+
<item target="color/icon_color_default" value="@color/top_level_icon_default"/>
+ <item target="color/qc_default_icon_color" value="@color/qc_default_icon_color"/>
+ <item target="color/top_level_injected_default_background" value="@android:color/transparent" />
<item target="color/top_level_icon_default" value="@color/top_level_icon_default"/>
<item target="color/top_level_accessibility_background" value="@color/top_level_icon_background"/>
<item target="color/top_level_applications_background" value="@color/top_level_icon_background"/>
@@ -32,6 +43,18 @@
<item target="color/top_level_sound_background" value="@color/top_level_icon_background"/>
<item target="color/top_level_system_background" value="@color/top_level_icon_background"/>
+ <item target="dimen/bluetooth_bonded_device_button_radius" value="@dimen/toggle_button_radius"/>
+ <item target="dimen/bluetooth_bonded_device_button_size" value="@dimen/toggle_button_size"/>
+ <item target="dimen/bluetooth_device_icon_size" value="@dimen/toggle_button_icon_size"/>
+ <item target="dimen/icon_size" value="@dimen/icon_size"/>
+ <item target="dimen/top_level_menu_width" value="@dimen/top_level_menu_width"/>
+
+ <item target="drawable/ic_bluetooth_button" value="@drawable/bluetooth_button"/>
+ <item target="drawable/ic_bluetooth_button_unavailable" value="@drawable/bluetooth_button_unavailable"/>
+ <item target="drawable/ic_bluetooth_media" value="@drawable/bluetooth_media_button"/>
+ <item target="drawable/ic_bluetooth_media_unavailable" value="@drawable/bluetooth_media_button_unavailable"/>
+ <item target="drawable/ic_bluetooth_phone" value="@drawable/bluetooth_phone_button"/>
+ <item target="drawable/ic_bluetooth_phone_unavailable" value="@drawable/bluetooth_phone_button_unavailable"/>
<item target="drawable/top_level_preference_background" value="@drawable/top_level_preference_background"/>
<item target="drawable/top_level_preference_highlight" value="@drawable/top_level_preference_highlight"/>
@@ -40,12 +63,4 @@
<item target="style/CarSettingsActivityDividerStyle" value="@style/CarSettingsActivityDividerStyle" />
<item target="style/CarSettingsFragmentContainerStyle" value="@style/CarSettingsFragmentContainerStyle" />
<item target="style/LockPattern" value="@style/LockPattern"/>
-
- <item target="animator/trans_left_in" value="@animator/trans_fade_in"/>
- <item target="animator/trans_left_out" value="@animator/trans_fade_out"/>
- <item target="animator/trans_right_in" value="@animator/trans_fade_in"/>
- <item target="animator/trans_right_out" value="@animator/trans_fade_out"/>
-
- <item target="dimen/top_level_menu_width" value="@dimen/top_level_menu_width"/>
- <item target="bool/config_top_level_injection_background_always_use_default" value="true" />
</overlay>
diff --git a/car_product/car_ui_portrait/rro/android/AndroidManifest.xml b/car_product/car_ui_portrait/rro/android/AndroidManifest.xml
index 41a3491..8149b69 100644
--- a/car_product/car_ui_portrait/rro/android/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/android/AndroidManifest.xml
@@ -19,7 +19,8 @@
<application android:hasCode="false" />
<overlay
android:targetPackage="android"
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
android:isStatic="true"
- android:priority="20"
- android:category="caruiportrait.rro"/>
+ android:priority="20"/>
</manifest>
diff --git a/car_product/car_ui_portrait/rro/android/res/anim-port/fade_in.xml b/car_product/car_ui_portrait/rro/android/res/anim/fade_in.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/anim-port/fade_in.xml
rename to car_product/car_ui_portrait/rro/android/res/anim/fade_in.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/anim-port/fade_out.xml b/car_product/car_ui_portrait/rro/android/res/anim/fade_out.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/anim-port/fade_out.xml
rename to car_product/car_ui_portrait/rro/android/res/anim/fade_out.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/btn_device_default.xml b/car_product/car_ui_portrait/rro/android/res/color/btn_device_default.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/btn_device_default.xml
rename to car_product/car_ui_portrait/rro/android/res/color/btn_device_default.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_button_background_color.xml b/car_product/car_ui_portrait/rro/android/res/color/car_button_background_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_button_background_color.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_button_background_color.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_button_colored_background_color.xml b/car_product/car_ui_portrait/rro/android/res/color/car_button_colored_background_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_button_colored_background_color.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_button_colored_background_color.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_button_text_color.xml b/car_product/car_ui_portrait/rro/android/res/color/car_button_text_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_button_text_color.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_button_text_color.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_button_toggle_background_color.xml b/car_product/car_ui_portrait/rro/android/res/color/car_button_toggle_background_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_button_toggle_background_color.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_button_toggle_background_color.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_button_toggle_text_color.xml b/car_product/car_ui_portrait/rro/android/res/color/car_button_toggle_text_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_button_toggle_text_color.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_button_toggle_text_color.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_switch_thumb.xml b/car_product/car_ui_portrait/rro/android/res/color/car_switch_thumb.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_switch_thumb.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_switch_thumb.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/car_switch_track.xml b/car_product/car_ui_portrait/rro/android/res/color/car_switch_track.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/car_switch_track.xml
rename to car_product/car_ui_portrait/rro/android/res/color/car_switch_track.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/overview_background.xml b/car_product/car_ui_portrait/rro/android/res/color/overview_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/overview_background.xml
rename to car_product/car_ui_portrait/rro/android/res/color/overview_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/overview_background_dark.xml b/car_product/car_ui_portrait/rro/android/res/color/overview_background_dark.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/overview_background_dark.xml
rename to car_product/car_ui_portrait/rro/android/res/color/overview_background_dark.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_on_accent_device_default.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_on_accent_device_default.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_on_accent_device_default.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_on_accent_device_default.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_primary_device_default_dark.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_primary_device_default_dark.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_primary_device_default_dark.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_primary_device_default_dark.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_primary_device_default_light.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_primary_device_default_light.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_primary_device_default_light.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_primary_device_default_light.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_secondary_device_default_dark.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_secondary_device_default_dark.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_secondary_device_default_dark.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_secondary_device_default_dark.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_secondary_device_default_light.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_secondary_device_default_light.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_secondary_device_default_light.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_secondary_device_default_light.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_tertiary_device_default_dark.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_tertiary_device_default_dark.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_tertiary_device_default_dark.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_tertiary_device_default_dark.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/color-port/text_color_tertiary_device_default_light.xml b/car_product/car_ui_portrait/rro/android/res/color/text_color_tertiary_device_default_light.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/color-port/text_color_tertiary_device_default_light.xml
rename to car_product/car_ui_portrait/rro/android/res/color/text_color_tertiary_device_default_light.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/alert_dialog_bg.xml b/car_product/car_ui_portrait/rro/android/res/drawable/alert_dialog_bg.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/alert_dialog_bg.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/alert_dialog_bg.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_button_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_borderless_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_button_borderless_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_borderless_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_button_borderless_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_colored_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_button_colored_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_colored_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_button_colored_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_toggle_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_button_toggle_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_button_toggle_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_button_toggle_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_checkbox.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_checkbox.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_checkbox.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_checkbox.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_checkbox_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_checkbox_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_checkbox_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_checkbox_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button1_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_dialog_button1_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button1_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_dialog_button1_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_dialog_button_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_dialog_button_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_dialog_button_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_seekbar_thumb.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_seekbar_thumb.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_seekbar_thumb.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_seekbar_thumb.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_seekbar_track.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_seekbar_track.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_seekbar_track.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_seekbar_track.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_switch_thumb.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_switch_thumb.xml
similarity index 72%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_switch_thumb.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_switch_thumb.xml
index f3e5686..6b967f8 100644
--- a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_switch_thumb.xml
+++ b/car_product/car_ui_portrait/rro/android/res/drawable/car_switch_thumb.xml
@@ -19,6 +19,16 @@
android:top="@dimen/car_switch_thumb_margin_size"
android:bottom="@dimen/car_switch_thumb_margin_size">
<shape android:shape="oval">
+ <solid android:color="@color/car_background"/>
+ <size
+ android:width="@dimen/car_switch_thumb_size"
+ android:height="@dimen/car_switch_thumb_size"/>
+ </shape>
+ </item>
+ <item android:gravity="center_vertical|fill_horizontal"
+ android:top="@dimen/car_switch_thumb_margin_size"
+ android:bottom="@dimen/car_switch_thumb_margin_size">
+ <shape android:shape="oval">
<solid android:color="@color/car_switch_thumb"/>
<size
android:width="@dimen/car_switch_thumb_size"
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/car_switch_track.xml b/car_product/car_ui_portrait/rro/android/res/drawable/car_switch_track.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/car_switch_track.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/car_switch_track.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/selectable_item_background.xml b/car_product/car_ui_portrait/rro/android/res/drawable/selectable_item_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/selectable_item_background.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/selectable_item_background.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/drawable-port/toast_frame.xml b/car_product/car_ui_portrait/rro/android/res/drawable/toast_frame.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/drawable-port/toast_frame.xml
rename to car_product/car_ui_portrait/rro/android/res/drawable/toast_frame.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog.xml b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog.xml
similarity index 82%
rename from car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog.xml
rename to car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog.xml
index b1c83eb..938ec46 100644
--- a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog.xml
+++ b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog.xml
@@ -22,6 +22,7 @@
android:maxHeight="916dp"
android:gravity="start|top"
android:background="@drawable/alert_dialog_bg"
+ android:padding="@dimen/car_alert_dialog_padding"
android:orientation="vertical">
<include layout="@layout/car_alert_dialog_title" />
<FrameLayout
@@ -29,9 +30,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@*android:id/topPanel"
- android:layout_marginStart="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginEnd="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginBottom="@dimen/car_alert_dialog_vertical_margin">
+ android:padding="@dimen/car_alert_dialog_padding">
<ScrollView
android:id="@*android:id/scrollView"
android:layout_width="match_parent"
@@ -45,7 +44,7 @@
android:id="@*android:id/textSpacerNoTitle"
android:visibility="gone"
android:layout_width="match_parent"
- android:layout_height="36dp"/>
+ android:layout_height="0dp"/>
<TextView
android:id="@android:id/message"
android:layout_width="match_parent"
@@ -64,10 +63,8 @@
android:id="@*android:id/customPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginEnd="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginBottom="@dimen/car_alert_dialog_vertical_margin"
- android:layout_below="@*android:id/contentPanel">
+ android:layout_below="@*android:id/contentPanel"
+ android:layout_margin="@dimen/car_alert_dialog_padding">
<FrameLayout
android:id="@android:id/custom"
android:layout_width="match_parent"
@@ -76,7 +73,8 @@
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/car_alert_dialog_horizontal_margin"
android:layout_below="@*android:id/customPanel"
+ android:layout_marginTop="@dimen/car_alert_dialog_padding"
+ android:layout_marginHorizontal="@dimen/car_alert_dialog_padding"
layout="@layout/car_alert_dialog_button_bar" />
</RelativeLayout>
diff --git a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_button_bar.xml b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_button_bar.xml
similarity index 77%
rename from car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_button_bar.xml
rename to car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_button_bar.xml
index b19f6c8..d8cb7c4 100644
--- a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_button_bar.xml
+++ b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_button_bar.xml
@@ -19,36 +19,31 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbarAlwaysDrawVerticalTrack="true"
- android:scrollIndicators="top|bottom"
+ android:scrollIndicators="none"
android:fillViewport="true"
style="?android:attr/buttonBarStyle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layoutDirection="locale"
- android:layout_marginStart="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginEnd="@dimen/car_alert_dialog_horizontal_margin"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@android:id/button3"
android:layout_width="match_parent"
android:layout_height="@dimen/button_layout_height"
- android:background="@drawable/car_dialog_button_background"
+ android:layout_marginBottom="@dimen/car_alert_dialog_button_spacing"
style="@style/CarDialogActionButton"/>
<Button
android:id="@android:id/button2"
android:layout_width="match_parent"
android:layout_height="@dimen/button_layout_height"
- android:layout_marginTop="@dimen/car_alert_dialog_button_margin"
- android:background="@drawable/car_dialog_button_background"
+ android:layout_marginBottom="@dimen/car_alert_dialog_button_spacing"
style="@style/CarDialogActionButton"/>
<Button
android:id="@android:id/button1"
android:layout_width="match_parent"
android:layout_height="@dimen/button_layout_height"
- android:layout_marginTop="@dimen/car_alert_dialog_button_margin"
- android:background="@drawable/car_dialog_button1_background"
+ android:layout_marginBottom="@dimen/car_alert_dialog_button_spacing"
style="@style/CarDialogActionButton1"/>
<Space
android:id="@*android:id/spacer"
diff --git a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_title.xml b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_title.xml
similarity index 74%
rename from car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_title.xml
rename to car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_title.xml
index 44a06ac..f3a7259 100644
--- a/car_product/car_ui_portrait/rro/android/res/layout-port-car/car_alert_dialog_title.xml
+++ b/car_product/car_ui_portrait/rro/android/res/layout-car/car_alert_dialog_title.xml
@@ -22,39 +22,36 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:padding="@dimen/car_alert_dialog_padding">
<!-- If the client uses a customTitle, it will be added here. -->
<RelativeLayout
android:id="@*android:id/title_template"
android:layout_width="match_parent"
- android:layout_height="@dimen/car_card_header_height"
- android:layout_marginStart="@dimen/car_alert_dialog_horizontal_margin"
- android:layout_marginEnd="@dimen/car_alert_dialog_horizontal_margin"
- android:orientation="horizontal">
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
<ImageView
android:id="@android:id/icon"
- android:layout_width="44dp"
- android:layout_height="44dp"
+ android:layout_width="@dimen/car_alert_dialog_icon_size"
+ android:layout_height="@dimen/car_alert_dialog_icon_size"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
+ android:layout_marginBottom="@dimen/car_alert_dialog_padding"
android:scaleType="fitCenter"
android:src="@null" />
+
<TextView
android:id="@*android:id/alertTitle"
- android:maxLines="1"
- android:ellipsize="none"
- android:textStyle="bold"
- android:textSize="36sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/icon"
android:textAlignment="center"
- android:textColor="@color/car_alert_dialog_message_text_color"
- android:layout_centerVertical="true"/>
+ android:layout_centerVertical="true"
+ style="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
</RelativeLayout>
<Space
android:id="@*android:id/titleDividerNoCustom"
android:visibility="gone"
android:layout_width="match_parent"
- android:layout_height="0dp" />
+ android:layout_height="0dp"/>
</LinearLayout>
diff --git a/car_product/car_ui_portrait/rro/android/res/layout-port/transient_notification_with_icon.xml b/car_product/car_ui_portrait/rro/android/res/layout-port/transient_notification_with_icon.xml
deleted file mode 100644
index 5a62f8b..0000000
--- a/car_product/car_ui_portrait/rro/android/res/layout-port/transient_notification_with_icon.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:maxWidth="@*android:dimen/toast_width"
- android:background="@android:drawable/toast_frame"
- android:elevation="@*android:dimen/toast_elevation"
- android:paddingStart="@dimen/car_portrait_ui_toast_margin"
- android:paddingEnd="@dimen/car_portrait_ui_toast_margin"
- android:paddingTop="@dimen/car_portrait_ui_toast_margin"
- android:paddingBottom="@dimen/car_portrait_ui_toast_margin"
- android:layout_marginBottom="@dimen/car_portrait_ui_toast_bottom_margin">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/car_portrait_ui_toast_icon_size"
- android:layout_height="@dimen/car_portrait_ui_toast_icon_size"
- android:layout_marginEnd="@dimen/car_portrait_ui_toast_margin"/>
- <TextView
- android:id="@android:id/message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Car.Toast"/>
-</LinearLayout>
-
diff --git a/car_product/car_ui_portrait/rro/android/res/layout-port/select_dialog_multichoice_material.xml b/car_product/car_ui_portrait/rro/android/res/layout/select_dialog_multichoice_material.xml
similarity index 93%
rename from car_product/car_ui_portrait/rro/android/res/layout-port/select_dialog_multichoice_material.xml
rename to car_product/car_ui_portrait/rro/android/res/layout/select_dialog_multichoice_material.xml
index 55f378a..3b52a37 100644
--- a/car_product/car_ui_portrait/rro/android/res/layout-port/select_dialog_multichoice_material.xml
+++ b/car_product/car_ui_portrait/rro/android/res/layout/select_dialog_multichoice_material.xml
@@ -24,5 +24,5 @@
android:textColor="@color/car_alert_dialog_message_text_color"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:drawableStart="?android:attr/listChoiceIndicatorMultiple"
+ android:drawableStart="@drawable/car_checkbox"
android:ellipsize="marquee" />
diff --git a/car_product/car_ui_portrait/rro/android/res/layout/transient_notification.xml b/car_product/car_ui_portrait/rro/android/res/layout/transient_notification.xml
new file mode 100644
index 0000000..e33fc0d
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/android/res/layout/transient_notification.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/car_portrait_ui_toast_bottom_margin">
+ <LinearLayout
+ android:layout_width="@*android:dimen/toast_width"
+ android:layout_height="wrap_content"
+ android:gravity="center">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:padding="@dimen/car_portrait_ui_toast_margin">
+ <TextView
+ android:id="@android:id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.Car.Toast"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/android/res/layout/transient_notification_with_icon.xml b/car_product/car_ui_portrait/rro/android/res/layout/transient_notification_with_icon.xml
new file mode 100644
index 0000000..83fcd50
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/android/res/layout/transient_notification_with_icon.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/car_portrait_ui_toast_bottom_margin">
+ <LinearLayout
+ android:layout_width="@*android:dimen/toast_width"
+ android:layout_height="wrap_content"
+ android:gravity="center">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:padding="@dimen/car_portrait_ui_toast_margin">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/car_portrait_ui_toast_icon_size"
+ android:layout_height="@dimen/car_portrait_ui_toast_icon_size"
+ android:layout_marginEnd="@dimen/car_portrait_ui_toast_icon_margin"/>
+ <TextView
+ android:id="@android:id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.Car.Toast"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/android/res/values-night/colors.xml
similarity index 92%
rename from car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/android/res/values-night/colors.xml
index 3b653f3..fde8432 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port-night/colors.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values-night/colors.xml
@@ -30,8 +30,8 @@
<color name="car_button_foreground">@color/car_on_secondary_container</color>
<!-- ****** Switch colors ***** -->
- <color name="car_switch_thumb_checked">@color/car_primary</color>
+ <color name="car_switch_thumb_checked">@color/car_on_primary</color>
<color name="car_switch_thumb_unchecked">@color/car_secondary</color>
- <color name="car_switch_track_checked">@color/car_on_primary</color>
+ <color name="car_switch_track_checked">@color/car_primary</color>
<color name="car_switch_track_unchecked">@color/car_secondary_container</color>
</resources>
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/config.xml b/car_product/car_ui_portrait/rro/android/res/values-port/config.xml
deleted file mode 100644
index 0a607b0..0000000
--- a/car_product/car_ui_portrait/rro/android/res/values-port/config.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Uses customized system bar controls -->
- <bool name="config_remoteInsetsControllerControlsSystemBars">true</bool>
-
- <!-- Device supports Sustained Performance Mode-->
- <bool name="config_sustainedPerformanceModeSupported">false</bool>
-
- <!-- Class name of the device specific implementation of DisplayAreaPolicy.Provider
- or empty if the default should be used. -->
- <string translatable="false" name="config_deviceSpecificDisplayAreaPolicyProvider">
- com.android.server.wm.CarDisplayAreaPolicyProvider
- </string>
-
- <!-- Colon separated list of package names that should be granted Notification Listener access -->
- <string name="config_defaultListenerAccessPackages" translatable="false">com.android.car.notification</string>
-
- <!-- Default body font family used across the system -->
- <string name="config_bodyFontFamily" translatable="false">roboto-regular</string>
-
- <!-- Default medium body font family used across the system -->
- <string name="config_bodyFontFamilyMedium" translatable="false">roboto-regular</string>
-
- <!-- Default headline font family used across the system -->
- <string name="config_headlineFontFamilyMedium" translatable="false">roboto-regular</string>
-
- <!-- Use round icon if it is provided by the package -->
- <bool name="config_useRoundIcon">true</bool>
-
- <!-- Setting the icon mask to circular path data -->
- <string name="config_icon_mask" translatable="false">"M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"</string>
-
- <!-- Whether the device allows users to start in background visible on displays.
- Should be false for most devices, except automotive vehicle with passenger displays. -->
- <bool name="config_multiuserVisibleBackgroundUsers">false</bool>
-
- <string name="config_recentsComponentName" translatable="false">
- com.android.car.portraitlauncher/.recents.PortraitCarRecentsActivity
- </string>
-</resources>
diff --git a/car_product/car_ui_portrait/rro/android/res/values-sw900dp-port/dimens.xml b/car_product/car_ui_portrait/rro/android/res/values-sw900dp/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/values-sw900dp-port/dimens.xml
rename to car_product/car_ui_portrait/rro/android/res/values-sw900dp/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/android/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/android/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/colors_car.xml b/car_product/car_ui_portrait/rro/android/res/values/colors_car.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/values-port/colors_car.xml
rename to car_product/car_ui_portrait/rro/android/res/values/colors_car.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/colors_device_default.xml b/car_product/car_ui_portrait/rro/android/res/values/colors_device_default.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/values-port/colors_device_default.xml
rename to car_product/car_ui_portrait/rro/android/res/values/colors_device_default.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/values/config.xml b/car_product/car_ui_portrait/rro/android/res/values/config.xml
index 7f7e2fc..9bae81b 100644
--- a/car_product/car_ui_portrait/rro/android/res/values/config.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/config.xml
@@ -17,5 +17,46 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- IME should not hide nav bar -->
- <bool name="config_hideNavBarForKeyboard">true</bool>
+ <bool name="config_hideNavBarForKeyboard">false</bool>
+
+ <!-- Uses customized system bar controls -->
+ <bool name="config_remoteInsetsControllerControlsSystemBars">true</bool>
+
+ <!-- Device supports Sustained Performance Mode-->
+ <bool name="config_sustainedPerformanceModeSupported">false</bool>
+
+ <!-- Colon separated list of package names that should be granted Notification Listener access -->
+ <string name="config_defaultListenerAccessPackages" translatable="false">com.android.car.notification</string>
+
+ <!-- Default body font family used across the system -->
+ <string name="config_bodyFontFamily" translatable="false">roboto-regular</string>
+
+ <!-- Default medium body font family used across the system -->
+ <string name="config_bodyFontFamilyMedium" translatable="false">roboto-regular</string>
+
+ <!-- Default headline font family used across the system -->
+ <string name="config_headlineFontFamilyMedium" translatable="false">roboto-regular</string>
+
+ <!-- Use round icon if it is provided by the package -->
+ <bool name="config_useRoundIcon">true</bool>
+
+ <!-- Setting the icon mask to circular path data -->
+ <string name="config_icon_mask" translatable="false">"M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"</string>
+
+ <!-- Whether the device allows users to start in background visible on displays.
+ Should be false for most devices, except automotive vehicle with passenger displays. -->
+ <bool name="config_multiuserVisibleBackgroundUsers">false</bool>
+
+ <string name="config_recentsComponentName" translatable="false">
+ com.android.car.portraitlauncher/.recents.PortraitCarRecentsActivity
+ </string>
+
+ <!-- Vertical position of a center of the letterboxed app window.
+ 0 corresponds to the upper side of the screen and 1 to the lower side. If given value < 0
+ or > 1 it is ignored and for non-tabletop mode central position is used (0.5); for
+ tabletop mode top (0.0) is used. -->
+ <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.5</item>
+
+ <!-- Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps. -->
+ <bool name="config_letterboxIsVerticalReachabilityEnabled">true</bool>
</resources>
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/android/res/values/dimens.xml
similarity index 95%
rename from car_product/car_ui_portrait/rro/android/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/android/res/values/dimens.xml
index 0ca15ab..b36520b 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/dimens.xml
@@ -42,14 +42,14 @@
<!-- ****** Toast dimens ***** -->
<!-- Toast corner radius -->
- <dimen name="toast_corner_radius">24dp</dimen>
+ <dimen name="toast_corner_radius">48dp</dimen>
<!-- Toast margin -->
<dimen name="car_portrait_ui_toast_margin">24dp</dimen>
<!-- Toast elevation -->
- <dimen name="toast_elevation">2dp</dimen>
+ <dimen name="toast_elevation">0dp</dimen>
<!-- Toast max width -->
- <dimen name="toast_width">760dp</dimen>
- <dimen name="toast_y_offset">168dp</dimen>
+ <dimen name="toast_width">1050dp</dimen>
+ <dimen name="toast_y_offset">268dp</dimen>
<dimen name="car_portrait_ui_toast_bottom_margin">32dp</dimen>
<!-- ****** Buttons dimens ***** -->
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/android/res/values/styles.xml
similarity index 85%
rename from car_product/car_ui_portrait/rro/android/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/android/res/values/styles.xml
index 14a28c0..fcf8d49 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/styles.xml
@@ -17,18 +17,9 @@
<resources>
- <style name="CarDialogActionButton" parent="TextAppearance.Car.Button.Medium">
- <item name="android:fontFamily">roboto-regular</item>
- <item name="android:textStyle">normal</item>
- <item name="android:lineHeight">40dp</item>
- <item name="android:background">@drawable/car_dialog_button_background</item>
- <item name="android:textColor">@color/car_alert_dialog_button_text_color</item>
- <item name="android:stateListAnimator">@null</item>
- </style>
+ <style name="CarDialogActionButton" parent="Widget.DeviceDefault.Button" />
- <style name="CarDialogActionButton1" parent="CarDialogActionButton">
- <item name="android:textColor">@color/car_alert_dialog_button1_text_color</item>
- </style>
+ <style name="CarDialogActionButton1" parent="Widget.DeviceDefault.Button.Colored" />
<style name="CarDialogMessage" parent="TextAppearance.Car.Body.Medium">
<item name="android:textStyle">normal</item>
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/styles_device_default.xml b/car_product/car_ui_portrait/rro/android/res/values/styles_device_default.xml
similarity index 96%
rename from car_product/car_ui_portrait/rro/android/res/values-port/styles_device_default.xml
rename to car_product/car_ui_portrait/rro/android/res/values/styles_device_default.xml
index b6ad44d..c5f057d 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port/styles_device_default.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/styles_device_default.xml
@@ -85,13 +85,7 @@
</style>
<style name="TextAppearance.DeviceDefault.DialogWindowTitle"
- parent="android:TextAppearance.Material.DialogWindowTitle">
- <item name="android:fontFamily">@string/config_headlineFontFamilyMedium</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@*android:dimen/car_body2_size</item>
- <item name="android:textColor">@*android:color/car_body2</item>
- </style>
-
+ parent="TextAppearance.DeviceDefault.WindowTitle" />
<style name="TextAppearance.DeviceDefault.Widget.Button"
parent="TextAppearance.Car.Button.Medium">
@@ -119,9 +113,18 @@
<item name="android:textColor">@color/car_button_toggle_text_color</item>
</style>
+ <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Car.Subhead.Large">
+ <item name="android:textStyle">normal</item>
+ <item name="android:fontWeight">500</item>
+ <item name="android:lineHeight">44dp</item>
+ <item name="android:textColor">@color/car_on_surface</item>
+ <item name="android:maxLines">1</item>
+ <item name="android:ellipsize">none</item>
+ <item name="android:stateListAnimator">@null</item>
+ </style>
+
<style name="Widget.DeviceDefault.TextView" parent="android:Widget.Material.TextView">
<item name="android:ellipsize">none</item>
- <item name="android:textSize">@*android:dimen/car_body1_size</item>
<item name="android:requiresFadingEdge">horizontal</item>
<item name="android:fadingEdgeLength">@*android:dimen/car_textview_fading_edge_length</item>
</style>
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/system_colors.xml b/car_product/car_ui_portrait/rro/android/res/values/system_colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/android/res/values-port/system_colors.xml
rename to car_product/car_ui_portrait/rro/android/res/values/system_colors.xml
diff --git a/car_product/car_ui_portrait/rro/android/res/values-port/themes_device_defaults.xml b/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
similarity index 93%
rename from car_product/car_ui_portrait/rro/android/res/values-port/themes_device_defaults.xml
rename to car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
index f1f9fba..6efe510 100644
--- a/car_product/car_ui_portrait/rro/android/res/values-port/themes_device_defaults.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
@@ -73,6 +73,9 @@
<item name="android:borderlessButtonStyle">@*android:style/Widget.DeviceDefault.Button.Borderless.Colored</item>
<item name="android:buttonBarButtonStyle">@*android:style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<item name="android:buttonStyle">@*android:style/Widget.DeviceDefault.Button</item>
+ <item name="android:buttonBarPositiveButtonStyle">@*android:style/Widget.DeviceDefault.Button.Colored</item>
+ <item name="android:buttonBarNegativeButtonStyle">@*android:style/Widget.DeviceDefault.Button</item>
+ <item name="android:buttonBarNeutralButtonStyle">@*android:style/Widget.DeviceDefault.Button</item>
<item name="android:selectableItemBackground">@*android:drawable/item_background</item>
<item name="android:windowTitleStyle">?android:attr/textAppearanceLarge</item>
<!-- Color palette -->
@@ -105,8 +108,15 @@
<item name="android:colorButtonNormal">@color/btn_device_default</item>
<item name="android:background">@android:color/transparent</item>
<item name="android:textColorPrimary">@color/car_alert_dialog_message_text_color</item>
+ <item name="android:textColorSecondary">@color/car_alert_dialog_message_text_color</item>
+ <item name="android:textColorTertiary">@color/car_alert_dialog_message_text_color</item>
<item name="android:windowBackground">@android:color/transparent</item>
+
+ <!-- Checkbox styling -->
+ <item name="android:listPreferredItemHeightSmall">@*android:dimen/car_single_line_list_item_height</item>
+ <item name="android:colorControlNormal">@color/btn_device_default</item>
+ <item name="android:colorControlActivated">@color/car_control_highlight</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="android:Theme.DeviceDefault.Dialog.Alert">
@@ -273,4 +283,9 @@
</style>
<style name="Theme.DeviceDefault.Light.Panel" parent="android:Theme.DeviceDefault.Panel"/>
+
+ <style name="Theme.DeviceDefault.SystemUI" parent="android:Theme.DeviceDefault.Light">
+ <item name="android:colorPrimary">@color/car_background</item>
+ <item name="*android:colorSurface">@color/car_on_background</item>
+ </style>
</resources>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/Android.bp b/car_product/car_ui_portrait/rro/car-ui-customizations/Android.bp
index 4eec20c..e86bac3 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/Android.bp
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/Android.bp
@@ -194,9 +194,9 @@
}
override_runtime_resource_overlay {
- name: "generated_caruiportrait_customization-com-google-car-ui-lib-sharedlibrary",
+ name: "generated_caruiportrait_customization-com-chassis-car-ui-plugin",
base: "GoogleCarUiPortraitBase",
- package_name: "com.android.car.ui.sharedlibrary.googlecaruiportrait.rro",
- target_package_name: "com.android.car.ui.sharedlibrary",
- category: "caruiportrait.caruisharedlib",
+ package_name: "com.chassis.car.ui.plugin.googlecaruiportrait.rro",
+ target_package_name: "com.chassis.car.ui.plugin",
+ category: "caruiportrait.caruiplugin",
}
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/AndroidManifest.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/AndroidManifest.xml
index b7afa3b..8ea9686 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/AndroidManifest.xml
@@ -23,7 +23,7 @@
android:targetPackage="will.be.replaced.too"
android:category="will.be.replaced"
android:resourcesMap="@xml/overlays"
- android:isStatic="false"
- android:requiredSystemPropertyName="ro.build.car_ui_rros_enabled"
- android:requiredSystemPropertyValue="true"/>
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true"/>
</manifest>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/product.mk b/car_product/car_ui_portrait/rro/car-ui-customizations/product.mk
index 26b4cbb..9287a27 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/product.mk
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/product.mk
@@ -38,7 +38,7 @@
generated_caruiportrait_customization-com-android-htmlviewer \
generated_caruiportrait_customization-com-android-permissioncontroller \
generated_caruiportrait_customization-com-google-android-car-portraitlauncher \
- generated_caruiportrait_customization-com-google-car-ui-lib-sharedlibrary \
+ generated_caruiportrait_customization-com-chassis-car-ui-plugin \
# This system property is used to enable the RROs on startup via
# the requiredSystemPropertyName/Value attributes in the manifest
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_summary_color.xml
similarity index 68%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_summary_color.xml
index 42b5d04..907b372 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_summary_color.xml
@@ -14,10 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- Copy of ?android:attr/textColorPrimary (frameworks/base/res/res/color/text_color_primary.xml)
+ but with a ux restricted state. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
-</selector>
\ No newline at end of file
+ <item app:state_ux_restricted="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_surface_variant"/>
+ <item android:color="@color/car_on_surface_variant"/>
+</selector>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_title_color.xml
similarity index 65%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
copy to car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_title_color.xml
index 42b5d04..22ce5dc 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/color-port/top_level_icon_default.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_preference_title_color.xml
@@ -14,10 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- Copy of ?android:attr/textColorPrimary (frameworks/base/res/res/color/text_color_primary.xml)
+ but with a ux restricted state. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:state_enabled="false"
android:alpha="?android:attr/disabledAlpha"
- android:color="@color/car_on_surface_variant"/>
- <item android:state_activated="true" android:color="@color/car_on_secondary_container" />
- <item android:color="@color/car_on_surface_variant" />
-</selector>
\ No newline at end of file
+ android:color="@color/car_on_surface"/>
+ <item app:state_ux_restricted="true"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/car_on_surface"/>
+ <item android:color="@color/car_on_surface"/>
+</selector>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_text_color_primary.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_text_color_primary.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_text_color_primary.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_text_color_primary.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_text_color_secondary.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_text_color_secondary.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_text_color_secondary.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_text_color_secondary.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_toolbar_tab_item_selector.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_toolbar_tab_item_selector.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/color-port/car_ui_toolbar_tab_item_selector.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/color/car_ui_toolbar_tab_item_selector.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_activity_background.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_activity_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_activity_background.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_activity_background.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_list_item_background.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_list_item_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_list_item_background.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_list_item_background.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_preference_primary_switch_background.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_preference_primary_switch_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_preference_primary_switch_background.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_preference_primary_switch_background.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_ic_down.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_ic_down.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_ic_down.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_ic_down.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_ic_up.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_ic_up.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_ic_up.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_ic_up.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_scrollbar_thumb.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_scrollbar_thumb.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_recyclerview_scrollbar_thumb.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_recyclerview_scrollbar_thumb.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_divider.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_divider.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_divider.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_divider.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_background.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_icon_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_background.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_icon_background.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_ripple.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_ripple.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_alert_dialog_title_with_subtitle.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_alert_dialog_title_with_subtitle.xml
similarity index 79%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_alert_dialog_title_with_subtitle.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_alert_dialog_title_with_subtitle.xml
index ef24a81..8001b41 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_alert_dialog_title_with_subtitle.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_alert_dialog_title_with_subtitle.xml
@@ -31,7 +31,7 @@
android:id="@+id/car_ui_alert_icon"
android:layout_width="@dimen/car_ui_dialog_icon_size"
android:layout_height="@dimen/car_ui_dialog_icon_size"
- android:layout_marginTop="@dimen/car_alert_dialog_vertical_margin"
+ android:layout_marginBottom="@dimen/car_alert_dialog_padding"
android:scaleType="fitCenter"
android:tint="@color/car_ui_text_color_primary"
android:visibility="gone"
@@ -40,25 +40,21 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/alert_dialog_margin"
- android:layout_marginEnd="@dimen/alert_dialog_margin"
- android:layout_marginTop="@dimen/car_alert_dialog_vertical_margin"
android:orientation="vertical">
<TextView
android:id="@+id/car_ui_alert_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:layout_marginBottom="@dimen/car_alert_dialog_vertical_margin"
- android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Title" />
+ style="@style/TextAppearance.CarUi.AlertDialog.Title" />
<TextView
android:id="@+id/car_ui_alert_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
- android:layout_marginBottom="@dimen/car_alert_dialog_vertical_margin"
- android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Subtitle"/>
+ android:layout_marginTop="@dimen/car_alert_dialog_padding"
+ style="@style/TextAppearance.CarUi.AlertDialog.Subtitle"/>
</LinearLayout>
<View
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_base_layout_toolbar.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_base_layout_toolbar.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_base_layout_toolbar.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_base_layout_toolbar.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_preference_primary_switch.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_preference_primary_switch.xml
similarity index 93%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_preference_primary_switch.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_preference_primary_switch.xml
index f6ee4a3..30ab7d9 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout-port/car_ui_preference_primary_switch.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/layout/car_ui_preference_primary_switch.xml
@@ -37,7 +37,7 @@
android:layout_marginEnd="@dimen/car_ui_preference_icon_margin_end"
android:layout_marginTop="@dimen/car_ui_preference_content_margin_top"
android:scaleType="fitCenter"
- style="@style/PreferenceCarUiIcon"/>
+ style="@style/Preference.CarUi.Icon"/>
<LinearLayout
android:layout_width="match_parent"
@@ -55,13 +55,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- style="@style/CarUiPreferencePrimaryTitleStyle"/>
+ android:textAppearance="@style/TextAppearance.CarUi.PreferencePrimaryTitle"/>
<com.android.car.ui.uxr.DrawableStateTextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@style/CarUiPreferencePrimarySummaryStyle"/>
+ android:textAppearance="@style/TextAppearance.CarUi.PreferencePrimarySummary"/>
</LinearLayout>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port-night/bools.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-night/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port-night/bools.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values-night/bools.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-night/colors.xml
similarity index 83%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values-night/colors.xml
index 8f8b919..eb9c3b2 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port-night/colors.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-night/colors.xml
@@ -16,6 +16,4 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<color name="car_ui_preference_primary_switch_background_color">@color/car_secondary_container</color>
- <color name="car_ui_preference_primary_title">@color/car_on_surface </color>
- <color name="car_ui_preference_primary_summary">@color/car_on_surface_variant </color>
</resources>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/attrs.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/attrs.xml
similarity index 93%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/attrs.xml
index e4a2533..b3f3b88 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/attrs.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/attrs.xml
@@ -75,4 +75,9 @@
<enum name="spread_inside" value="1"/>
<enum name="packed" value="2"/>
</attr>
+ <attr name="layout_constraintVertical_chainStyle" format="enum">
+ <enum name="spread" value="0"/>
+ <enum name="spread_inside" value="1"/>
+ <enum name="packed" value="2"/>
+ </attr>
</resources>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/bools.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/dimens.xml
similarity index 95%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/dimens.xml
index fa046d1..d91c5f0 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/dimens.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/dimens.xml
@@ -26,6 +26,7 @@
<dimen name="car_ui_margin">112dp</dimen>
<dimen name="car_ui_toolbar_margin">@dimen/car_ui_margin</dimen>
<dimen name="car_ui_padding_3">16dp</dimen>
+ <dimen name="car_ui_divider_width">1dp</dimen>
<dimen name="car_ui_toolbar_title_margin_start">@dimen/car_ui_padding_3</dimen>
<dimen name="car_ui_toolbar_search_height">0dp</dimen>
<dimen name="car_ui_list_item_height">88dp</dimen>
@@ -42,7 +43,7 @@
<dimen name="car_ui_list_item_text_no_icon_start_margin">0dp</dimen>
- <dimen name="car_ui_dialog_icon_size">50dp</dimen>
+ <dimen name="car_ui_dialog_icon_size">64dp</dimen>
<dimen name="car_ui_preference_content_margin_top">16dp</dimen>
<dimen name="car_ui_preference_content_margin_bottom">16dp</dimen>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/drawables.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/drawables.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/drawables.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/drawables.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/styles.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/styles.xml
similarity index 76%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/styles.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/styles.xml
index aac6206..3801f63 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/styles.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/styles.xml
@@ -31,11 +31,18 @@
</style>
<style name="TextAppearance.CarUi.PreferenceSummary" parent="TextAppearance.Car.Body.Small">
- <item name="android:textColor">@color/car_on_surface_variant</item>
+ <item name="android:textColor">@color/car_ui_preference_summary_color</item>
</style>
- <style name="TextAppearance.CarUi.PreferenceTitle" parent="TextAppearance.Car.Body.Small">
- <item name="android:textColor">@color/car_on_surface_variant</item>
+ <style name="TextAppearance.CarUi.PreferenceTitle" parent="TextAppearance.Car.Body.Large">
+ <item name="android:textColor">@color/car_ui_preference_title_color</item>
+ </style>
+
+ <style name="TextAppearance.CarUi.PreferencePrimaryTitle" parent="TextAppearance.Car.Body.Large">
+ <item name="android:textColor">@color/car_on_primary_container</item>
+ </style>
+ <style name="TextAppearance.CarUi.PreferencePrimarySummary" parent="TextAppearance.Car.Body.Small">
+ <item name="android:textColor">@color/car_on_primary_container</item>
</style>
<style name="TextAppearance.CarUi.Widget" parent="android:TextAppearance.DeviceDefault.Widget">
@@ -77,13 +84,17 @@
<item name="android:tint">@color/car_on_surface</item>
<item name="android:background">@drawable/car_ui_toolbar_menu_item_icon_ripple</item>
</style>
-
- <style name="PreferenceCarUiIcon"/>
-
- <style name="CarUiPreferencePrimaryTitleStyle" parent="TextAppearance.CarUi.PreferenceSummary">
- <item name="android:textColor">@color/car_ui_preference_primary_title</item>
+ <style name="Widget.CarUi.SeekbarPreference"/>
+ <style name="Widget.CarUi.SeekbarPreference.Seekbar">
+ <item name="android:minHeight">76dp</item>
+ <item name="android:background">@null</item>
+ <item name="android:clickable">false</item>
+ <item name="android:focusable">false</item>
</style>
- <style name="CarUiPreferencePrimarySummaryStyle" parent="TextAppearance.CarUi.PreferenceSummary">
- <item name="android:textColor">@color/car_ui_preference_primary_summary</item>
+ <style name="Preference"/>
+ <style name="Preference.CarUI" parent="Preference"/>
+ <style name="Preference.CarUi.Icon" parent="Preference.CarUI"/>
+ <style name="Preference.CarUi.Divider" parent="Preference.CarUI">
+ <item name="android:background">@color/car_ui_preference_two_action_divider_color</item>
</style>
</resources>
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/themes.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/themes.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/themes.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/themes.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/values.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/values/values.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-customizations/res/values-port/values.xml
rename to car_product/car_ui_portrait/rro/car-ui-customizations/res/values/values.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-customizations/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/car-ui-customizations/res/xml/overlays.xml
index 58d65d8..cbc3c59 100644
--- a/car_product/car_ui_portrait/rro/car-ui-customizations/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-customizations/res/xml/overlays.xml
@@ -59,7 +59,8 @@
<item target="bool/car_ui_scrollbar_enable" value="@bool/car_ui_scrollbar_enable" />
<item target="bool/car_ui_is_light_theme" value="@bool/car_ui_is_light_theme" />
- <item target="style/Preference.CarUi.Icon" value="@style/PreferenceCarUiIcon" />
+ <item target="style/Preference.CarUi.Icon" value="@style/Preference.CarUi.Icon" />
+ <item target="style/Preference.CarUi.Divider" value="@style/Preference.CarUi.Divider" />
<item target="style/TextAppearance.CarUi.AlertDialog.Title" value="@style/TextAppearance.CarUi.AlertDialog.Title" />
<item target="style/TextAppearance.CarUi.AlertDialog.Subtitle" value="@style/TextAppearance.CarUi.AlertDialog.Subtitle" />
<item target="style/TextAppearance.CarUi.PreferenceCategoryTitle" value="@style/TextAppearance.CarUi.PreferenceCategoryTitle" />
@@ -69,6 +70,8 @@
<item target="style/TextAppearance.CarUi.Widget.Toolbar" value="@style/TextAppearance.CarUi.Widget.Toolbar" />
<item target="style/TextAppearance.CarUi.Widget.Toolbar.Title" value="@style/TextAppearance.CarUi.Widget.Toolbar.Title" />
+ <item target="style/Widget.CarUi.SeekbarPreference.Seekbar" value="@style/Widget.CarUi.SeekbarPreference.Seekbar" />
+
<item target="attr/state_ux_restricted" value="@attr/state_ux_restricted"/>
<!-- Toolbar -->
@@ -113,4 +116,5 @@
<item target="attr/layout_goneMarginStart" value="@attr/layout_goneMarginStart"/>
<item target="attr/layout_goneMarginEnd" value="@attr/layout_goneMarginEnd"/>
<item target="attr/layout_constraintHorizontal_chainStyle" value="@attr/layout_constraintHorizontal_chainStyle"/>
+ <item target="attr/layout_constraintVertical_chainStyle" value="@attr/layout_constraintVertical_chainStyle"/>
</overlay>
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/AndroidManifest.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/AndroidManifest.xml
index d2cda31..e0444f6 100644
--- a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/AndroidManifest.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/AndroidManifest.xml
@@ -23,7 +23,7 @@
android:targetName="car-ui-lib"
android:targetPackage="will.be.replaced.too"
android:resourcesMap="@xml/overlays"
- android:isStatic="false"
- android:requiredSystemPropertyName="ro.build.car_ui_rros_enabled"
- android:requiredSystemPropertyValue="true"/>
+ android:requiredSystemPropertyName="car.ui.config"
+ android:requiredSystemPropertyValue="rickybobby"
+ android:isStatic="true"/>
</manifest>
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_text_color_primary.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_text_color_primary.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_text_color_primary.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_text_color_primary.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_text_color_secondary.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_text_color_secondary.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_text_color_secondary.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_text_color_secondary.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_toolbar_tab_item_selector.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_toolbar_tab_item_selector.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/car_ui_toolbar_tab_item_selector.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/car_ui_toolbar_tab_item_selector.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/tab_side_indicator_color.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/tab_side_indicator_color.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color-port/tab_side_indicator_color.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/color/tab_side_indicator_color.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/car_ui_toolbar_menu_item_divider.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/car_ui_toolbar_menu_item_divider.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/car_ui_toolbar_menu_item_divider.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/car_ui_toolbar_menu_item_divider.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_ripple.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/car_ui_toolbar_menu_item_icon_ripple.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/car_ui_toolbar_menu_item_icon_ripple.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/tab_background.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable-port/tab_background.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout-port/car_ui_base_layout_toolbar.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout-port/car_ui_base_layout_toolbar.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout-port/car_ui_toolbar_tab_item.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_toolbar_tab_item.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout-port/car_ui_toolbar_tab_item.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_toolbar_tab_item.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port-night/colors.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-night/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port-night/colors.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-night/colors.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/attrs.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/attrs.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/attrs.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/attrs.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/bools.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/bools.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/bools.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/bools.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/colors.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/colors.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/colors.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/colors.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/dimens.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/dimens.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/dimens.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/dimens.xml
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/themes.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/themes.xml
similarity index 100%
rename from car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-port/themes.xml
rename to car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/themes.xml
diff --git a/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk b/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
index 24b082e..7e634e2 100644
--- a/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
+++ b/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
@@ -35,7 +35,6 @@
CarUiPortraitNotificationRRO \
CarUiPortraitRadioRRO \
CarUiPortraitSettingsRRO \
- CarUiPortraitSystemUIQcRRO \
ifneq ($(INCLUDE_SEAHAWK_ONLY_RROS),)
PRODUCT_PACKAGES += \
@@ -43,58 +42,46 @@
endif
# Set necessary framework configs for SUW to run at boot.
-ifneq ($(filter $(TARGET_PRODUCT), gcar_ui_portrait_suw),)
+ifneq ($(filter $(TARGET_PRODUCT), gcar_portrait_suw),)
PRODUCT_PACKAGES += \
CarUiPortraitSettingsProviderEmuRRO
endif
+PRODUCT_PRODUCT_PROPERTIES += \
+ car.ui.config=rickybobby
+
PORTRAIT_RRO_PACKAGES := com.android.car.calendar.googlecaruiportrait.rro; \
- com.android.car.carlauncher.apps.caruiportrait.rro; \
com.android.car.carlauncher.googlecaruiportrait.rro; \
- com.android.car.carlauncher.media.caruiportrait.rro; \
- com.android.car.caruiportrait.rro; \
com.android.car.developeroptions.googlecaruiportrait.rro; \
- com.android.car.dialer.caruiportrait.rro; \
com.android.car.dialer.googlecaruiportrait.rro; \
com.android.car.dialer.googlecaruiportrait.toolbar.rro; \
com.android.car.faceenroll.googlecaruiportrait.rro; \
com.android.car.home.googlecaruiportrait.rro; \
com.android.car.linkviewer.googlecaruiportrait.rro; \
- com.android.car.media.caruiportrait.rro; \
- com.android.car.media.common.caruiportrait.rro; \
com.android.car.media.googlecaruiportrait.rro; \
com.android.car.media.googlecaruiportrait.toolbar.rro; \
- com.nadroid.car.messenger.caruiportrait.rro; \
com.android.car.messenger.googlecaruiportrait.rro; \
com.android.car.messenger.googlecaruiportrait.toolbar.rro; \
- com.android.car.notification.caruiportrait.rro; \
com.android.car.portraitlauncher.googlecaruiportrait.rro; \
- com.android.car.portraitlauncher.rro; \
- com.android.car.radio.caruiportrait.rro; \
com.android.car.radio.googlecaruiportrait.rro; \
com.android.car.radio.googlecaruiportrait.toolbar.rro; \
com.android.car.rotaryplayground.googlecaruiportrait.rro; \
- com.android.car.settings.caruiportrait.rro; \
com.android.car.settings.googlecaruiportrait.rro; \
com.android.car.systemupdater.googlecaruiportrait.rro; \
com.android.car.themeplayground.googlecaruiportrait.rro; \
com.android.car.ui.paintbooth.googlecaruiportrait.rro; \
com.android.car.ui.paintbooth.googlecaruiportrait.rro; \
- com.android.car.ui.sharedlibrary.googlecaruiportrait.rro; \
com.android.car.voicecontrol.googlecaruiportrait.rro; \
com.android.htmlviewer.googlecaruiportrait.rro; \
com.android.managedprovisioning.googlecaruiportrait.rro; \
com.android.permissioncontroller.googlecaruiportrait.rro; \
- com.android.providers.settings.caruiportrait.emu.rro; \
- com.android.providers.settings.caruiportrait.rro; \
com.android.settings.intelligence.googlecaruiportrait.rro; \
- com.android.systemui.caruiportrait.qc.rro; \
com.android.vending.googlecaruiportrait.rro; \
+ com.chassis.car.ui.plugin.googlecaruiportrait.rro; \
com.google.android.apps.automotive.inputmethod.dev.googlecaruiportrait.rro; \
com.google.android.apps.automotive.inputmethod.googlecaruiportrait.rro; \
com.google.android.apps.automotive.templates.host.googlecaruiportrait.rro; \
com.google.android.carassistant.googlecaruiportrait.rro; \
- com.google.android.car.evs.caruiportrait.rro; \
com.google.android.carui.ats.googlecaruiportrait.rro; \
com.google.android.companiondevicesupport.googlecaruiportrait.rro; \
com.google.android.embedded.projection.googlecaruiportrait.rro; \
diff --git a/car_product/overlay-visual/frameworks/base/core/res/res/values/dimens.xml b/car_product/overlay-visual/frameworks/base/core/res/res/values/dimens.xml
index a2f9abf..a2bcafb 100644
--- a/car_product/overlay-visual/frameworks/base/core/res/res/values/dimens.xml
+++ b/car_product/overlay-visual/frameworks/base/core/res/res/values/dimens.xml
@@ -26,8 +26,10 @@
<dimen name="navigation_bar_height">96dp</dimen>
<dimen name="navigation_bar_height_landscape">96dp</dimen>
- <!-- Notification icon size in the status bar. -->
+ <!-- Original dp unit notification icon size in the status bar. -->
<dimen name="status_bar_icon_size">32dp</dimen>
+ <!-- New sp unit notification icon size in the status bar. -->
+ <dimen name="status_bar_icon_size_sp">32sp</dimen>
<!-- System status icon size in the status bar. -->
<dimen name="status_bar_system_icon_size">32dp</dimen>
diff --git a/car_product/overlay-visual/frameworks/base/core/res/res/values/themes_device_defaults.xml b/car_product/overlay-visual/frameworks/base/core/res/res/values/themes_device_defaults.xml
index a7defb4..476e93b 100644
--- a/car_product/overlay-visual/frameworks/base/core/res/res/values/themes_device_defaults.xml
+++ b/car_product/overlay-visual/frameworks/base/core/res/res/values/themes_device_defaults.xml
@@ -164,8 +164,13 @@
<style name="Theme.DeviceDefault.Settings" parent="android:Theme.DeviceDefault"/>
<style name="Theme.DeviceDefault.Settings.NoActionBar" parent="android:Theme.DeviceDefault.NoActionBar"/>
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="android:Theme.DeviceDefault"/>
- <!-- DeviceDefault theme for the default system theme. -->
+ <!-- This theme is applied as the app base. Attributes not defined in app/ activity themes will fallthrough to this
+ theme. This makes it app facing, regardless if the app is directly using it or a theme that inherits from it. -->
+ <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="android:Theme.DeviceDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <!-- DeviceDefault theme for the default system theme. -->
<style name="Theme.DeviceDefault.System" parent="android:Theme.DeviceDefault.Light.DarkActionBar" />
<!-- Theme used for the intent picker activity. -->
@@ -324,4 +329,10 @@
</style>
<style name="Theme.DeviceDefault.Light.Panel" parent="android:Theme.DeviceDefault.Panel"/>
+
+ <style name="Theme.DeviceDefault.SystemUI" parent="android:Theme.DeviceDefault.Light">
+ <item name="*android:materialColorOnSurface">@*android:color/text_color_primary</item>
+ <item name="*android:materialColorOnSecondaryFixed">@*android:color/text_color_primary</item>
+ <item name="*android:materialColorSurfaceContainer">@android:color/black</item>
+ </style>
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 3184983..f431e59c 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -205,4 +205,8 @@
<string name="config_recentsComponentName" translatable="false">
com.android.car.carlauncher/.recents.CarRecentsActivity
</string>
+
+ <!-- Disable the "Share" action item shown in the context menu that appears upon long-pressing
+ on selected text. -->
+ <bool name="config_textShareSupported">false</bool>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/CarSystemUI/res/values/config.xml b/car_product/overlay/frameworks/base/packages/CarSystemUI/res/values/config.xml
index d8d8516..54d68fe 100644
--- a/car_product/overlay/frameworks/base/packages/CarSystemUI/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/packages/CarSystemUI/res/values/config.xml
@@ -19,6 +19,8 @@
<!-- Resource overrides for car system ui. -->
<resources>
+ <!-- The dreams feature (screensavers) is not supported in android auto -->
+ <bool name="config_dreamsSupported">false</bool>
<!-- Notifications on the car should not show the gear icon. Swiping should only dismiss the
cards. -->
diff --git a/car_product/rro/CarSystemUIPassengerRRO/res/values/config.xml b/car_product/rro/CarSystemUIPassengerRRO/res/values/config.xml
index 36c8772..fa825a4 100644
--- a/car_product/rro/CarSystemUIPassengerRRO/res/values/config.xml
+++ b/car_product/rro/CarSystemUIPassengerRRO/res/values/config.xml
@@ -17,4 +17,7 @@
<string-array name="config_quickControlsEntryPointIconControllers" translatable="false">
<item>com.android.systemui.car.statusicon.ui.QuickControlsStatusIconListController</item>
</string-array>
+
+ <string name="config_notificationPanelViewMediator" translatable="false">
+ com.android.systemui.car.notification.BottomNotificationPanelViewMediator</string>
</resources>
diff --git a/car_product/rro/CarSystemUIPassengerRRO/res/xml/overlays.xml b/car_product/rro/CarSystemUIPassengerRRO/res/xml/overlays.xml
index 2d6b3d6..520569a 100644
--- a/car_product/rro/CarSystemUIPassengerRRO/res/xml/overlays.xml
+++ b/car_product/rro/CarSystemUIPassengerRRO/res/xml/overlays.xml
@@ -20,6 +20,7 @@
<item target="array/config_quickControlsEntryPointIconControllers" value="@array/config_quickControlsEntryPointIconControllers"/>
<item target="bool/config_enableTopSystemBar" value="false"/>
+ <item target="string/config_notificationPanelViewMediator" value="@string/config_notificationPanelViewMediator" />
<item target="id/notifications" value="@id/notifications" />
<item target="string/system_bar_notifications_label" value="@string/system_bar_notifications_label"/>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/Android.bp b/car_product/rro/CarSystemUIRRO/Android.bp
similarity index 64%
rename from car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/Android.bp
rename to car_product/rro/CarSystemUIRRO/Android.bp
index 663fdbd..9b4ea2a 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/Android.bp
+++ b/car_product/rro/CarSystemUIRRO/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Android Open Source Project
+// Copyright (C) 2023 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.
@@ -14,16 +14,12 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
-
-runtime_resource_overlay {
- name: "CarUiPortraitSystemUIQcRRO",
- manifest: "AndroidManifest.xml",
+android_app {
+ name: "CarSystemUIRRO",
resource_dirs: ["res"],
- static_libs: [
- "car-portrait-ui-common"
+ sdk_version: "current",
+ aaptflags: [
+ "--no-resource-deduping",
+ "--no-resource-removal"
],
- package_name: "com.android.systemui.caruiportrait.qc.rro",
- target_package_name: "com.android.systemui",
- category: "caruiportrait.systemui",
}
-
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/sharedlibrary/src/main/AndroidManifest.xml b/car_product/rro/CarSystemUIRRO/AndroidManifest.xml
similarity index 72%
rename from car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/sharedlibrary/src/main/AndroidManifest.xml
rename to car_product/rro/CarSystemUIRRO/AndroidManifest.xml
index fc49e94..317e163 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitCarUiProxy/sharedlibrary/src/main/AndroidManifest.xml
+++ b/car_product/rro/CarSystemUIRRO/AndroidManifest.xml
@@ -14,13 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.car.ui.sharedlibrary">
- <!-- This is set to 28 matching that of the static lib -->
- <uses-sdk
- android:minSdkVersion="28"/>
- <application>
- <library android:name="com.android.car.ui.sharedlibrary" />
- </application>
-</manifest>
\ No newline at end of file
+ package="com.android.car.systemui.rro">
+ <application android:hasCode="false"/>
+ <overlay android:targetName="CarSystemUI"
+ android:targetPackage="com.android.systemui"
+ android:resourcesMap="@xml/overlays"
+ android:isStatic="true" />
+</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/xml/overlays.xml b/car_product/rro/CarSystemUIRRO/res/xml/overlays.xml
similarity index 80%
rename from car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/xml/overlays.xml
rename to car_product/rro/CarSystemUIRRO/res/xml/overlays.xml
index 9b20f46..30ef6e7 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitCarQcLibRRO/res/xml/overlays.xml
+++ b/car_product/rro/CarSystemUIRRO/res/xml/overlays.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2023 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.
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+
<overlay>
- <item target="color/qc_start_icon_color" value="@color/qc_start_icon_color"/>
+ <item target="style/Theme.SystemUI.Dialog" value="@android:Theme.DeviceDefault.Dialog"/>
</overlay>
diff --git a/car_product/sepolicy/private/carservice_app.te b/car_product/sepolicy/private/carservice_app.te
index 5e2492f..70c8879 100644
--- a/car_product/sepolicy/private/carservice_app.te
+++ b/car_product/sepolicy/private/carservice_app.te
@@ -105,6 +105,9 @@
# Allow reading vehicle-specific configuration
get_prop(carservice_app, vehicle_hal_prop)
+# Allow writing carwatchdog configuration
+set_prop(carservice_app, carwatchdog_config_prop)
+
# Allow CarWatchdogService to access car watchdog daemon
carwatchdog_client_domain(carservice_app)
diff --git a/cpp/evs/manager/1.1/Enumerator.cpp b/cpp/evs/manager/1.1/Enumerator.cpp
index 1e01395..3c00f02 100644
--- a/cpp/evs/manager/1.1/Enumerator.cpp
+++ b/cpp/evs/manager/1.1/Enumerator.cpp
@@ -238,17 +238,32 @@
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
Return<void> Enumerator::getCameraList(getCameraList_cb list_cb) {
+ if (!mPermissionChecker->processHasPermissionsForEvs()) {
+ list_cb({});
+ return Void();
+ }
+
hardware::hidl_vec<CameraDesc_1_0> cameraList;
- mServiceFactory->getService()->getCameraList_1_1([&cameraList](auto cameraList_1_1) {
- cameraList.resize(cameraList_1_1.size());
- unsigned i = 0;
- for (auto&& cam : cameraList_1_1) {
- cameraList[i++] = cam.v1;
+ mServiceFactory->getService()->getCameraList([&cameraList](auto enumeratedCameras) {
+ cameraList.resize(enumeratedCameras.size());
+ unsigned count = 0;
+ for (auto&& cam : enumeratedCameras) {
+ cameraList[count++] = cam;
}
});
- list_cb(cameraList);
+ // Update the cached device list.
+ for (auto&& desc : cameraList) {
+ auto it = mCameraDevices.find(desc.cameraId);
+ if (it != mCameraDevices.end()) {
+ it->second.v1 = desc;
+ } else {
+ CameraDesc desc_1_1 = { .v1 = desc };
+ mCameraDevices.emplace(desc.cameraId, desc_1_1);
+ }
+ }
+ list_cb(cameraList);
return Void();
}
diff --git a/cpp/evs/manager/1.1/HalCamera.cpp b/cpp/evs/manager/1.1/HalCamera.cpp
index 584f22e..06da890 100644
--- a/cpp/evs/manager/1.1/HalCamera.cpp
+++ b/cpp/evs/manager/1.1/HalCamera.cpp
@@ -234,14 +234,16 @@
}
Return<EvsResult> HalCamera::clientStreamStarting() {
- Return<EvsResult> result = EvsResult::OK;
+ {
+ std::lock_guard lock(mFrameMutex);
+ if (mStreamState != STOPPED) {
+ return EvsResult::OK;
+ }
- if (mStreamState == STOPPED) {
mStreamState = RUNNING;
- result = mHwCamera->startVideoStream(this);
}
- return result;
+ return mHwCamera->startVideoStream(this);
}
void HalCamera::cancelCaptureRequestFromClientLocked(std::deque<struct FrameRequest>* requests,
@@ -261,6 +263,11 @@
std::lock_guard<std::mutex> lock(mFrameMutex);
cancelCaptureRequestFromClientLocked(mNextRequests, client);
cancelCaptureRequestFromClientLocked(mCurrentRequests, client);
+
+ if (mStreamState != RUNNING) {
+ // We are being stopped or stopped already.
+ return;
+ }
}
// Do we still have a running client?
@@ -274,7 +281,10 @@
// If not, then stop the hardware stream
if (!stillRunning) {
- mStreamState = STOPPING;
+ {
+ std::lock_guard lock(mFrameMutex);
+ mStreamState = STOPPING;
+ }
mHwCamera->stopVideoStream();
}
}
@@ -437,6 +447,7 @@
Return<void> HalCamera::notify(const EvsEventDesc& event) {
LOG(DEBUG) << "Received an event id: " << static_cast<int32_t>(event.aType);
if (event.aType == EvsEventType::STREAM_STOPPED) {
+ std::lock_guard lock(mFrameMutex);
// This event happens only when there is no more active client.
if (mStreamState != STOPPING) {
LOG(WARNING) << "Stream stopped unexpectedly";
diff --git a/cpp/evs/manager/1.1/HalCamera.h b/cpp/evs/manager/1.1/HalCamera.h
index e0d4d3c..6e27889 100644
--- a/cpp/evs/manager/1.1/HalCamera.h
+++ b/cpp/evs/manager/1.1/HalCamera.h
@@ -117,7 +117,7 @@
STOPPED,
RUNNING,
STOPPING,
- } mStreamState = STOPPED;
+ } mStreamState GUARDED_BY(mFrameMutex) = STOPPED;
struct FrameRecord {
uint32_t frameId;
diff --git a/cpp/evs/manager/aidl/include/HalCamera.h b/cpp/evs/manager/aidl/include/HalCamera.h
index fbbc1a5..35bb65b 100644
--- a/cpp/evs/manager/aidl/include/HalCamera.h
+++ b/cpp/evs/manager/aidl/include/HalCamera.h
@@ -53,10 +53,7 @@
mId(deviceId),
mStreamConfig(cfg),
mTimeCreatedMs(::android::uptimeMillis()),
- mUsageStats(new CameraUsageStats(recordId)) {
- mCurrentRequests = &mFrameRequests[0];
- mNextRequests = &mFrameRequests[1];
- }
+ mUsageStats(new CameraUsageStats(recordId)) {}
virtual ~HalCamera();
@@ -104,7 +101,7 @@
STOPPED,
RUNNING,
STOPPING,
- } mStreamState = STOPPED;
+ } mStreamState GUARDED_BY(mFrameMutex) = STOPPED;
struct FrameRecord {
uint32_t frameId;
@@ -121,14 +118,9 @@
int64_t timestamp = -1;
};
- void cancelCaptureRequestFromClientLocked(std::deque<FrameRequest>* requests,
- const VirtualCamera* client) REQUIRES(mFrameMutex);
-
// synchronization
mutable std::mutex mFrameMutex;
- std::deque<FrameRequest> mFrameRequests[2] GUARDED_BY(mFrameMutex);
- std::deque<FrameRequest>* mCurrentRequests PT_GUARDED_BY(mFrameMutex);
- std::deque<FrameRequest>* mNextRequests PT_GUARDED_BY(mFrameMutex);
+ std::deque<FrameRequest> mNextRequests GUARDED_BY(mFrameMutex);
// Time this object was created
int64_t mTimeCreatedMs;
diff --git a/cpp/evs/manager/aidl/src/HalCamera.cpp b/cpp/evs/manager/aidl/src/HalCamera.cpp
index b9c5ac6..a7421ff 100644
--- a/cpp/evs/manager/aidl/src/HalCamera.cpp
+++ b/cpp/evs/manager/aidl/src/HalCamera.cpp
@@ -200,35 +200,34 @@
req.timestamp = lastTimestamp;
std::lock_guard<std::mutex> lock(mFrameMutex);
- mNextRequests->push_back(req);
+ mNextRequests.push_back(req);
}
ScopedAStatus HalCamera::clientStreamStarting() {
- if (mStreamState != STOPPED) {
- return ScopedAStatus::ok();
- }
-
- mStreamState = RUNNING;
- return mHwCamera->startVideoStream(ref<HalCamera>());
-}
-
-void HalCamera::cancelCaptureRequestFromClientLocked(std::deque<struct FrameRequest>* requests,
- const VirtualCamera* client) {
- auto it = requests->begin();
- while (it != requests->end()) {
- if (it->client.lock().get() == client) {
- requests->erase(it);
- return;
+ {
+ std::lock_guard lock(mFrameMutex);
+ if (mStreamState != STOPPED) {
+ return ScopedAStatus::ok();
}
- ++it;
+
+ mStreamState = RUNNING;
}
+ return mHwCamera->startVideoStream(ref<HalCamera>());
}
void HalCamera::clientStreamEnding(const VirtualCamera* client) {
{
std::lock_guard<std::mutex> lock(mFrameMutex);
- cancelCaptureRequestFromClientLocked(mNextRequests, client);
- cancelCaptureRequestFromClientLocked(mCurrentRequests, client);
+ if (mStreamState != RUNNING) {
+ // We are being stopped or stopped already.
+ return;
+ }
+
+ mNextRequests.erase(std::remove_if(mNextRequests.begin(), mNextRequests.end(),
+ [client](const auto& r) {
+ return r.client.lock().get() == client;
+ }),
+ mNextRequests.end());
}
// Do we still have a running client?
@@ -242,7 +241,10 @@
// If not, then stop the hardware stream
if (!stillRunning) {
- mStreamState = STOPPING;
+ {
+ std::lock_guard lock(mFrameMutex);
+ mStreamState = STOPPING;
+ }
auto status = mHwCamera->stopVideoStream();
if (!status.isOk()) {
LOG(WARNING) << "Failed to stop a video stream, error = "
@@ -304,66 +306,68 @@
// but this must be derived from current framerate.
constexpr int64_t kThreshold = 16'000; // ms
unsigned frameDeliveries = 0;
+ std::deque<FrameRequest> currentRequests;
{
- // Handle frame requests from v1.1 clients
std::lock_guard<std::mutex> lock(mFrameMutex);
- std::swap(mCurrentRequests, mNextRequests);
- while (!mCurrentRequests->empty()) {
- auto req = mCurrentRequests->front();
- mCurrentRequests->pop_front();
- std::shared_ptr<VirtualCamera> vCam = req.client.lock();
- if (!vCam) {
- // Ignore a client already dead.
- continue;
- }
+ currentRequests.insert(currentRequests.end(),
+ std::make_move_iterator(mNextRequests.begin()),
+ std::make_move_iterator(mNextRequests.end()));
+ mNextRequests.clear();
+ }
- if (timestamp - req.timestamp < kThreshold) {
- // Skip current frame because it arrives too soon.
- LOG(DEBUG) << "Skips a frame from " << getId();
- mNextRequests->push_back(req);
-
- // Reports a skipped frame
- mUsageStats->framesSkippedToSync();
- } else {
- if (!vCam->deliverFrame(buffers[0])) {
- LOG(WARNING) << getId() << " failed to forward the buffer to " << vCam.get();
- } else {
- LOG(DEBUG) << getId() << " forwarded the buffer #" << buffers[0].bufferId
- << " to " << vCam.get() << " from " << this;
- ++frameDeliveries;
- }
- }
+ while (!currentRequests.empty()) {
+ auto req = currentRequests.front();
+ currentRequests.pop_front();
+ std::shared_ptr<VirtualCamera> vCam = req.client.lock();
+ if (!vCam) {
+ // Ignore a client already dead.
+ continue;
}
- if (frameDeliveries < 1) {
- // If none of our clients could accept the frame, then return it
- // right away.
- LOG(INFO) << "Trivially rejecting frame (" << buffers[0].bufferId << ") from "
- << getId() << " with no acceptance";
- if (!mHwCamera->doneWithFrame(buffers).isOk()) {
- LOG(WARNING) << "Failed to return buffers";
- }
+ if (timestamp - req.timestamp < kThreshold) {
+ // Skip current frame because it arrives too soon.
+ LOG(DEBUG) << "Skips a frame from " << getId();
+ mUsageStats->framesSkippedToSync();
+ continue;
+ }
- // Reports a returned buffer
- mUsageStats->framesReturned(buffers);
+ if (!vCam->deliverFrame(buffers[0])) {
+ LOG(WARNING) << getId() << " failed to forward the buffer to " << vCam.get();
} else {
- // Add an entry for this frame in our tracking list.
- unsigned i;
- for (i = 0; i < mFrames.size(); ++i) {
- if (mFrames[i].refCount == 0) {
- break;
- }
- }
-
- if (i == mFrames.size()) {
- mFrames.push_back(buffers[0].bufferId);
- } else {
- mFrames[i].frameId = buffers[0].bufferId;
- }
- mFrames[i].refCount = frameDeliveries;
+ LOG(DEBUG) << getId() << " forwarded the buffer #" << buffers[0].bufferId << " to "
+ << vCam.get() << " from " << this;
+ ++frameDeliveries;
}
}
+ if (frameDeliveries < 1) {
+ // If none of our clients could accept the frame, then return it
+ // right away.
+ LOG(INFO) << "Trivially rejecting frame (" << buffers[0].bufferId << ") from " << getId()
+ << " with no acceptance";
+ if (!mHwCamera->doneWithFrame(buffers).isOk()) {
+ LOG(WARNING) << "Failed to return buffers";
+ }
+
+ // Reports a returned buffer
+ mUsageStats->framesReturned(buffers);
+ } else {
+ // Add an entry for this frame in our tracking list.
+ unsigned i;
+ for (i = 0; i < mFrames.size(); ++i) {
+ if (mFrames[i].refCount == 0) {
+ break;
+ }
+ }
+
+ if (i == mFrames.size()) {
+ mFrames.push_back(buffers[0].bufferId);
+ } else {
+ mFrames[i].frameId = buffers[0].bufferId;
+ }
+ mFrames[i].refCount = frameDeliveries;
+ }
+
return ScopedAStatus::ok();
}
@@ -371,6 +375,7 @@
LOG(DEBUG) << "Received an event id: " << static_cast<int32_t>(event.aType);
if (event.aType == EvsEventType::STREAM_STOPPED) {
// This event happens only when there is no more active client.
+ std::lock_guard lock(mFrameMutex);
if (mStreamState != STOPPING) {
LOG(WARNING) << "Stream stopped unexpectedly";
}
diff --git a/cpp/evs/manager/aidl/stats/src/CameraUsageStats.cpp b/cpp/evs/manager/aidl/stats/src/CameraUsageStats.cpp
index cc21acf..5c23646 100644
--- a/cpp/evs/manager/aidl/stats/src/CameraUsageStats.cpp
+++ b/cpp/evs/manager/aidl/stats/src/CameraUsageStats.cpp
@@ -36,7 +36,12 @@
void CameraUsageStats::updateFrameStatsOnArrivalLocked(const std::vector<BufferDesc>& bufs) {
const auto now = ::android::uptimeMillis();
for (const auto& b : bufs) {
- mBufferHistory.insert_or_assign(b.bufferId, now);
+ auto it = mBufferHistory.find(b.bufferId);
+ if (it != mBufferHistory.end()) {
+ it->second.timestamp = now;
+ } else {
+ mBufferHistory.insert({b.bufferId, now});
+ }
}
}
diff --git a/cpp/powerpolicy/server/carpowerpolicyd.xml b/cpp/powerpolicy/server/carpowerpolicyd.xml
index 07f65d6..b7ca6d7 100644
--- a/cpp/powerpolicy/server/carpowerpolicyd.xml
+++ b/cpp/powerpolicy/server/carpowerpolicyd.xml
@@ -16,7 +16,7 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.frameworks.automotive.powerpolicy</name>
- <version>1</version>
+ <version>2</version>
<interface>
<name>ICarPowerPolicyServer</name>
<instance>default</instance>
diff --git a/cpp/powerpolicy/server/tests/data/valid_power_policy_custom_components.xml b/cpp/powerpolicy/server/tests/data/valid_power_policy_custom_components.xml
index cebc67b..1288cf4 100644
--- a/cpp/powerpolicy/server/tests/data/valid_power_policy_custom_components.xml
+++ b/cpp/powerpolicy/server/tests/data/valid_power_policy_custom_components.xml
@@ -15,13 +15,6 @@
-->
<powerPolicy version="1.0">
-
- <customComponents>
- <customComponent value="1000">CUSTOM_COMPONENT_1000</customComponent>
- <customComponent value="1003">CUSTOM_COMPONENT_SPECIAL_SENSOR</customComponent>
- <customComponent value="1002">CUSTOM_COMPONENT_AUX_INPUT</customComponent>
- </customComponents>
-
<policyGroups>
<policyGroup id="basic_policy_group">
<defaultPolicy state="WaitForVHAL" id="policy_id_other_on"/>
@@ -94,4 +87,9 @@
</policy>
</systemPolicyOverrides>
+ <customComponents>
+ <customComponent value="1000">CUSTOM_COMPONENT_1000</customComponent>
+ <customComponent value="1003">CUSTOM_COMPONENT_SPECIAL_SENSOR</customComponent>
+ <customComponent value="1002">CUSTOM_COMPONENT_AUX_INPUT</customComponent>
+ </customComponents>
</powerPolicy>
diff --git a/cpp/watchdog/sepolicy/private/carwatchdog.te b/cpp/watchdog/sepolicy/private/carwatchdog.te
index 5f2d304..98cbddd 100644
--- a/cpp/watchdog/sepolicy/private/carwatchdog.te
+++ b/cpp/watchdog/sepolicy/private/carwatchdog.te
@@ -9,6 +9,9 @@
binder_use(carwatchdogd)
binder_service(carwatchdogd)
+# Read carwatchdog configuration properties
+get_prop(carwatchdogd, carwatchdog_config_prop)
+
# Configration to communicate with VHAL.
hwbinder_use(carwatchdogd)
get_prop(carwatchdogd, hwservicemanager_prop)
diff --git a/cpp/watchdog/sepolicy/private/property.te b/cpp/watchdog/sepolicy/private/property.te
new file mode 100644
index 0000000..054f75f
--- /dev/null
+++ b/cpp/watchdog/sepolicy/private/property.te
@@ -0,0 +1,2 @@
+# Properties used only in /system
+system_internal_prop(carwatchdog_config_prop)
diff --git a/cpp/watchdog/sepolicy/private/property_contexts b/cpp/watchdog/sepolicy/private/property_contexts
new file mode 100644
index 0000000..80be408
--- /dev/null
+++ b/cpp/watchdog/sepolicy/private/property_contexts
@@ -0,0 +1,2 @@
+# CarWatchdog property contexts
+carwatchdog.sync_resource_usage_stats_with_carservice.enabled u:object_r:carwatchdog_config_prop:s0 exact bool
diff --git a/cpp/watchdog/server/src/IoOveruseMonitor.h b/cpp/watchdog/server/src/IoOveruseMonitor.h
index 5c77785..6754f18 100644
--- a/cpp/watchdog/server/src/IoOveruseMonitor.h
+++ b/cpp/watchdog/server/src/IoOveruseMonitor.h
@@ -75,9 +75,6 @@
// Dumps the help text.
virtual bool dumpHelpText(int fd) const = 0;
- // Notifies that CarWatchdogService was registered
- virtual void onCarWatchdogServiceRegistered() = 0;
-
// Below API is from internal/ICarWatchdog.aidl. Please refer to the AIDL for description.
virtual android::base::Result<void> updateResourceOveruseConfigurations(
const std::vector<
diff --git a/cpp/watchdog/server/src/PerformanceProfiler.cpp b/cpp/watchdog/server/src/PerformanceProfiler.cpp
index d85dd16..484f850 100644
--- a/cpp/watchdog/server/src/PerformanceProfiler.cpp
+++ b/cpp/watchdog/server/src/PerformanceProfiler.cpp
@@ -486,10 +486,15 @@
return {};
}
+void PerformanceProfiler::onCarWatchdogServiceRegistered() {
+ Mutex::Autolock lock(mMutex);
+ mDoSendResourceUsageStats =
+ sysprop::syncResourceUsageStatsWithCarServiceEnabled().value_or(false);
+}
+
Result<void> PerformanceProfiler::onBoottimeCollection(
time_t time, const wp<UidStatsCollectorInterface>& uidStatsCollector,
- const wp<ProcStatCollectorInterface>& procStatCollector,
- [[maybe_unused]] ResourceStats* resourceStats) {
+ const wp<ProcStatCollectorInterface>& procStatCollector, ResourceStats* resourceStats) {
const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
@@ -497,15 +502,15 @@
return result;
}
Mutex::Autolock lock(mMutex);
- return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
- procStatCollectorSp, &mBoottimeCollection);
+ return processLocked(time, SystemState::NORMAL_MODE, std::unordered_set<std::string>(),
+ uidStatsCollectorSp, procStatCollectorSp, &mBoottimeCollection,
+ resourceStats);
}
Result<void> PerformanceProfiler::onPeriodicCollection(
- time_t time, [[maybe_unused]] SystemState systemState,
+ time_t time, SystemState systemState,
const wp<UidStatsCollectorInterface>& uidStatsCollector,
- const wp<ProcStatCollectorInterface>& procStatCollector,
- [[maybe_unused]] ResourceStats* resourceStats) {
+ const wp<ProcStatCollectorInterface>& procStatCollector, ResourceStats* resourceStats) {
const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
clearExpiredSystemEventCollections(time);
@@ -514,8 +519,8 @@
return result;
}
Mutex::Autolock lock(mMutex);
- return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
- procStatCollectorSp, &mPeriodicCollection);
+ return processLocked(time, systemState, std::unordered_set<std::string>(), uidStatsCollectorSp,
+ procStatCollectorSp, &mPeriodicCollection, resourceStats);
}
Result<void> PerformanceProfiler::onUserSwitchCollection(
@@ -544,8 +549,9 @@
if (mUserSwitchCollections.size() > mMaxUserSwitchEvents) {
mUserSwitchCollections.erase(mUserSwitchCollections.begin());
}
- return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
- procStatCollectorSp, &mUserSwitchCollections.back());
+ return processLocked(time, SystemState::NORMAL_MODE, std::unordered_set<std::string>(),
+ uidStatsCollectorSp, procStatCollectorSp, &mUserSwitchCollections.back(),
+ /*resourceStats=*/nullptr);
}
Result<void> PerformanceProfiler::onWakeUpCollection(
@@ -558,16 +564,15 @@
return result;
}
Mutex::Autolock lock(mMutex);
- return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
- procStatCollectorSp, &mWakeUpCollection);
+ return processLocked(time, SystemState::NORMAL_MODE, std::unordered_set<std::string>(),
+ uidStatsCollectorSp, procStatCollectorSp, &mWakeUpCollection,
+ /*resourceStats=*/nullptr);
}
Result<void> PerformanceProfiler::onCustomCollection(
- time_t time, [[maybe_unused]] SystemState systemState,
- const std::unordered_set<std::string>& filterPackages,
+ time_t time, SystemState systemState, const std::unordered_set<std::string>& filterPackages,
const wp<UidStatsCollectorInterface>& uidStatsCollector,
- const wp<ProcStatCollectorInterface>& procStatCollector,
- [[maybe_unused]] ResourceStats* resourceStats) {
+ const wp<ProcStatCollectorInterface>& procStatCollector, ResourceStats* resourceStats) {
const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
@@ -575,14 +580,18 @@
return result;
}
Mutex::Autolock lock(mMutex);
- return processLocked(time, filterPackages, uidStatsCollectorSp, procStatCollectorSp,
- &mCustomCollection);
+ return processLocked(time, systemState, filterPackages, uidStatsCollectorSp,
+ procStatCollectorSp, &mCustomCollection, resourceStats);
}
+// TODO(b/266008677): Use the systemState variable to correctly attribute the mode of the I/O
+// usage stats when collecting the resource usage stats.
Result<void> PerformanceProfiler::processLocked(
- time_t time, const std::unordered_set<std::string>& filterPackages,
+ time_t time, [[maybe_unused]] SystemState systemState,
+ const std::unordered_set<std::string>& filterPackages,
const sp<UidStatsCollectorInterface>& uidStatsCollector,
- const sp<ProcStatCollectorInterface>& procStatCollector, CollectionInfo* collectionInfo) {
+ const sp<ProcStatCollectorInterface>& procStatCollector, CollectionInfo* collectionInfo,
+ [[maybe_unused]] ResourceStats* resourceStats) {
if (collectionInfo->maxCacheSize == 0) {
return Error() << "Maximum cache size cannot be 0";
}
diff --git a/cpp/watchdog/server/src/PerformanceProfiler.h b/cpp/watchdog/server/src/PerformanceProfiler.h
index 769b2f8..decf409 100644
--- a/cpp/watchdog/server/src/PerformanceProfiler.h
+++ b/cpp/watchdog/server/src/PerformanceProfiler.h
@@ -194,7 +194,8 @@
mUserSwitchCollections({}),
mWakeUpCollection({}),
mCustomCollection({}),
- mLastMajorFaults(0) {}
+ mLastMajorFaults(0),
+ mDoSendResourceUsageStats(false) {}
~PerformanceProfiler() { terminate(); }
@@ -203,6 +204,8 @@
// Implements DataProcessorInterface.
android::base::Result<void> onSystemStartup() override;
+ void onCarWatchdogServiceRegistered() override;
+
android::base::Result<void> onBoottimeCollection(
time_t time, const android::wp<UidStatsCollectorInterface>& uidStatsCollector,
const android::wp<ProcStatCollectorInterface>& procStatCollector,
@@ -252,10 +255,12 @@
private:
// Processes the collected data.
android::base::Result<void> processLocked(
- time_t time, const std::unordered_set<std::string>& filterPackages,
+ time_t time, SystemState systemState,
+ const std::unordered_set<std::string>& filterPackages,
const android::sp<UidStatsCollectorInterface>& uidStatsCollector,
const android::sp<ProcStatCollectorInterface>& procStatCollector,
- CollectionInfo* collectionInfo);
+ CollectionInfo* collectionInfo,
+ aidl::android::automotive::watchdog::internal::ResourceStats* resourceStats);
// Processes per-UID performance data.
void processUidStatsLocked(const std::unordered_set<std::string>& filterPackages,
@@ -308,6 +313,9 @@
// major faults since last collection.
uint64_t mLastMajorFaults GUARDED_BY(mMutex);
+ // Enables the sending of resource usage stats to CarService.
+ bool mDoSendResourceUsageStats GUARDED_BY(mMutex);
+
friend class WatchdogPerfService;
// For unit tests.
diff --git a/cpp/watchdog/server/src/WatchdogInternalHandler.cpp b/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
index 680e812..95cd285 100644
--- a/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
+++ b/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
@@ -202,7 +202,6 @@
auto status = mWatchdogServiceHelper->registerService(service);
if (status.isOk()) {
mWatchdogPerfService->onCarWatchdogServiceRegistered();
- mIoOveruseMonitor->onCarWatchdogServiceRegistered();
}
return status;
}
diff --git a/cpp/watchdog/server/src/WatchdogPerfService.cpp b/cpp/watchdog/server/src/WatchdogPerfService.cpp
index a953925..41c594c 100644
--- a/cpp/watchdog/server/src/WatchdogPerfService.cpp
+++ b/cpp/watchdog/server/src/WatchdogPerfService.cpp
@@ -305,6 +305,9 @@
void WatchdogPerfService::onCarWatchdogServiceRegistered() {
Mutex::Autolock lock(mMutex);
+ for (const auto& processor : mDataProcessors) {
+ processor->onCarWatchdogServiceRegistered();
+ }
if (mUnsentResourceStats.empty()) {
return;
}
diff --git a/cpp/watchdog/server/src/WatchdogPerfService.h b/cpp/watchdog/server/src/WatchdogPerfService.h
index 15e7703..f0a123a 100644
--- a/cpp/watchdog/server/src/WatchdogPerfService.h
+++ b/cpp/watchdog/server/src/WatchdogPerfService.h
@@ -88,6 +88,8 @@
// Callback to perform actions (such as clearing stats from previous system startup events)
// before starting boot-time or wake-up collections.
virtual android::base::Result<void> onSystemStartup() = 0;
+ // Callback to perform actions once CarWatchdogService is registered.
+ virtual void onCarWatchdogServiceRegistered() = 0;
// Callback to process the data collected during boot-time.
virtual android::base::Result<void> onBoottimeCollection(
time_t time, const android::wp<UidStatsCollectorInterface>& uidStatsCollector,
diff --git a/cpp/watchdog/server/sysprop/WatchdogProperties.sysprop b/cpp/watchdog/server/sysprop/WatchdogProperties.sysprop
index e154106..2057bed 100644
--- a/cpp/watchdog/server/sysprop/WatchdogProperties.sysprop
+++ b/cpp/watchdog/server/sysprop/WatchdogProperties.sysprop
@@ -131,3 +131,12 @@
access: Readonly
prop_name: "ro.carwatchdog.io_overuse_warn_percentage"
}
+
+# Flag for sending resource usage stats from daemon to CarService.
+prop {
+ api_name: "syncResourceUsageStatsWithCarServiceEnabled"
+ type: Boolean
+ scope: Internal
+ access: Writeonce
+ prop_name: "carwatchdog.sync_resource_usage_stats_with_carservice.enabled"
+}
diff --git a/cpp/watchdog/server/tests/MockDataProcessor.h b/cpp/watchdog/server/tests/MockDataProcessor.h
index 6df8299..a9f597b 100644
--- a/cpp/watchdog/server/tests/MockDataProcessor.h
+++ b/cpp/watchdog/server/tests/MockDataProcessor.h
@@ -34,6 +34,7 @@
MOCK_METHOD(android::base::Result<void>, init, (), (override));
MOCK_METHOD(void, terminate, (), (override));
MOCK_METHOD(android::base::Result<void>, onSystemStartup, (), (override));
+ MOCK_METHOD(void, onCarWatchdogServiceRegistered, (), (override));
MOCK_METHOD(android::base::Result<void>, onBoottimeCollection,
(time_t, const wp<UidStatsCollectorInterface>&,
const wp<ProcStatCollectorInterface>&,
diff --git a/cpp/watchdog/server/tests/PerformanceProfilerTest.cpp b/cpp/watchdog/server/tests/PerformanceProfilerTest.cpp
index a32b966..91cc7eb 100644
--- a/cpp/watchdog/server/tests/PerformanceProfilerTest.cpp
+++ b/cpp/watchdog/server/tests/PerformanceProfilerTest.cpp
@@ -60,6 +60,7 @@
constexpr int kTestTopNStatsPerSubcategory = 5;
constexpr int kTestMaxUserSwitchEvents = 3;
constexpr std::chrono::seconds kTestSystemEventDataCacheDurationSec = 60s;
+constexpr time_t kTestNow = static_cast<time_t>(1'683'270'000);
MATCHER_P(IoStatsViewEq, expected, "") {
return ExplainMatchResult(AllOf(Field("bytes", &UserPackageStats::IoStatsView::bytes,
@@ -531,6 +532,10 @@
mCollector->mSystemEventDataCacheDurationSec = value;
}
+ void setSendResourceUsageStatsEnabled(bool enable) {
+ mCollector->mDoSendResourceUsageStats = enable;
+ }
+
const CollectionInfo& getBoottimeCollectionInfo() {
Mutex::Autolock lock(mCollector->mMutex);
return mCollector->mBoottimeCollection;
@@ -574,6 +579,7 @@
mCollectorPeer->setTopNStatsPerSubcategory(kTestTopNStatsPerSubcategory);
mCollectorPeer->setMaxUserSwitchEvents(kTestMaxUserSwitchEvents);
mCollectorPeer->setSystemEventDataCacheDuration(kTestSystemEventDataCacheDurationSec);
+ mCollectorPeer->setSendResourceUsageStatsEnabled(true);
}
void TearDown() override {
@@ -623,10 +629,10 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onBoottimeCollection(now, mMockUidStatsCollector,
- mMockProcStatCollector, &resourceStats));
+ ResourceStats actualResourceStats = {};
+ ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
+ mMockProcStatCollector,
+ &actualResourceStats));
const auto actual = mCollectorPeer->getBoottimeCollectionInfo();
@@ -654,9 +660,8 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ASSERT_RESULT_OK(
- mCollector->onWakeUpCollection(now, mMockUidStatsCollector, mMockProcStatCollector));
+ ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
+ mMockProcStatCollector));
const auto actual = mCollectorPeer->getWakeUpCollectionInfo();
@@ -684,12 +689,11 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onBoottimeCollection(now, mMockUidStatsCollector,
+ ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
mMockProcStatCollector, &resourceStats));
- ASSERT_RESULT_OK(
- mCollector->onWakeUpCollection(now, mMockUidStatsCollector, mMockProcStatCollector));
+ ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
+ mMockProcStatCollector));
auto actualBoottimeCollection = mCollectorPeer->getBoottimeCollectionInfo();
auto actualWakeUpCollection = mCollectorPeer->getWakeUpCollectionInfo();
@@ -716,8 +720,7 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(now, 100, 101, mMockUidStatsCollector,
+ ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, 100, 101, mMockUidStatsCollector,
mMockProcStatCollector));
const auto& actualInfos = mCollectorPeer->getUserSwitchCollectionInfos();
@@ -791,8 +794,8 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(nextUidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(nextProcStatInfo));
- now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(now, 100, 101, mMockUidStatsCollector,
+ ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow + 2, 100, 101,
+ mMockUidStatsCollector,
mMockProcStatCollector));
auto& continuationActualInfos = mCollectorPeer->getUserSwitchCollectionInfos();
@@ -843,10 +846,8 @@
});
}
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
-
for (userid_t userId = 100; userId < 100 + kTestMaxUserSwitchEvents; ++userId) {
- ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(now, userId, userId + 1,
+ ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, userId, userId + 1,
mMockUidStatsCollector,
mMockProcStatCollector));
}
@@ -875,7 +876,7 @@
});
expectedEvents.erase(expectedEvents.begin());
- ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(now, userId, userId + 1,
+ ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, userId, userId + 1,
mMockUidStatsCollector,
mMockProcStatCollector));
@@ -894,11 +895,11 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
+ ResourceStats actualResourceStats = {};
+ ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
mMockUidStatsCollector,
- mMockProcStatCollector, &resourceStats));
+ mMockProcStatCollector,
+ &actualResourceStats));
const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
@@ -927,11 +928,10 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onCustomCollection(now, SystemState::NORMAL_MODE, {},
+ ResourceStats actualResourceStats = {};
+ ASSERT_RESULT_OK(mCollector->onCustomCollection(kTestNow, SystemState::NORMAL_MODE, {},
mMockUidStatsCollector, mMockProcStatCollector,
- &resourceStats));
+ &actualResourceStats));
const auto actual = mCollectorPeer->getCustomCollectionInfo();
@@ -1105,11 +1105,11 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(firstUidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(firstProcStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
+ ResourceStats actualResourceStats = {};
+ ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
mMockUidStatsCollector,
- mMockProcStatCollector, &resourceStats));
+ mMockProcStatCollector,
+ &actualResourceStats));
auto [secondUidStats, secondUserPackageSummaryStats] = sampleUidStats(/*multiplier=*/2);
const auto [secondProcStatInfo, secondSystemSummaryStats] = sampleProcStat(/*multiplier=*/2);
@@ -1123,9 +1123,10 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(secondUidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(secondProcStatInfo));
- ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
+ ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
mMockUidStatsCollector,
- mMockProcStatCollector, &resourceStats));
+ mMockProcStatCollector,
+ &actualResourceStats));
const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
@@ -1154,9 +1155,8 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
ResourceStats resourceStats = {};
- ASSERT_RESULT_OK(mCollector->onBoottimeCollection(now, mMockUidStatsCollector,
+ ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
mMockProcStatCollector, &resourceStats));
auto actual = mCollectorPeer->getBoottimeCollectionInfo();
@@ -1165,7 +1165,8 @@
// Call |onPeriodicCollection| 1 hour past the last boot-time collection event.
ASSERT_RESULT_OK(
- mCollector->onPeriodicCollection(now + kTestSystemEventDataCacheDurationSec.count(),
+ mCollector->onPeriodicCollection(kTestNow +
+ kTestSystemEventDataCacheDurationSec.count(),
SystemState::NORMAL_MODE, mMockUidStatsCollector,
mMockProcStatCollector, &resourceStats));
@@ -1181,9 +1182,8 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- ASSERT_RESULT_OK(
- mCollector->onWakeUpCollection(now, mMockUidStatsCollector, mMockProcStatCollector));
+ ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
+ mMockProcStatCollector));
auto actual = mCollectorPeer->getWakeUpCollectionInfo();
@@ -1193,7 +1193,8 @@
// Call |onPeriodicCollection| 1 hour past the last wake-up collection event.
ASSERT_RESULT_OK(
- mCollector->onPeriodicCollection(now + kTestSystemEventDataCacheDurationSec.count(),
+ mCollector->onPeriodicCollection(kTestNow +
+ kTestSystemEventDataCacheDurationSec.count(),
SystemState::NORMAL_MODE, mMockUidStatsCollector,
mMockProcStatCollector, &resourceStats));
@@ -1209,8 +1210,7 @@
EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
- time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- time_t updatedNow = now;
+ time_t updatedNow = kTestNow;
for (userid_t userId = 100; userId < 100 + kTestMaxUserSwitchEvents; ++userId) {
ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(updatedNow, userId, userId + 1,
@@ -1223,7 +1223,7 @@
EXPECT_THAT(actual.size(), kTestMaxUserSwitchEvents);
- updatedNow = now + kTestSystemEventDataCacheDurationSec.count();
+ updatedNow = kTestNow + kTestSystemEventDataCacheDurationSec.count();
ResourceStats resourceStats = {};
for (int i = 1; i <= kTestMaxUserSwitchEvents; ++i) {
ASSERT_RESULT_OK(mCollector->onPeriodicCollection(updatedNow, SystemState::NORMAL_MODE,
diff --git a/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp b/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
index ea89063..a509e5d 100644
--- a/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
+++ b/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
@@ -200,7 +200,6 @@
EXPECT_CALL(*mMockWatchdogPerfService, registerDataProcessor(Eq(mMockIoOveruseMonitor)))
.WillOnce(Return(Result<void>()));
EXPECT_CALL(*mMockWatchdogPerfService, onCarWatchdogServiceRegistered()).Times(1);
- EXPECT_CALL(*mMockIoOveruseMonitor, onCarWatchdogServiceRegistered()).Times(1);
std::shared_ptr<ICarWatchdogServiceForSystem> service =
SharedRefBase::make<ICarWatchdogServiceForSystemDefault>();
diff --git a/cpp/watchdog/server/tests/WatchdogPerfServiceTest.cpp b/cpp/watchdog/server/tests/WatchdogPerfServiceTest.cpp
index 56f9a93..2cc1173 100644
--- a/cpp/watchdog/server/tests/WatchdogPerfServiceTest.cpp
+++ b/cpp/watchdog/server/tests/WatchdogPerfServiceTest.cpp
@@ -353,6 +353,7 @@
// TODO(b/266008677): Add more data to the ResourceStats.
std::optional<ResourceUsageStats> boottimeResourceUsageStats =
std::make_optional<ResourceUsageStats>({});
+
EXPECT_CALL(*mMockUidStatsCollector, collect()).Times(1);
EXPECT_CALL(*mMockProcStatCollector, collect()).Times(1);
EXPECT_CALL(*mMockDataProcessor,
@@ -1616,6 +1617,7 @@
// Expect because the next pollCache call will result in an onPeriodicMonitor call
// because no message is sent to process unsent resource stats
EXPECT_CALL(*mMockDataProcessor, onPeriodicMonitor(_, _, _)).Times(1);
+ EXPECT_CALL(*mMockDataProcessor, onCarWatchdogServiceRegistered()).Times(1);
EXPECT_CALL(*mMockWatchdogServiceHelper, onLatestResourceStats(_)).Times(0);
mService->onCarWatchdogServiceRegistered();
@@ -1632,6 +1634,7 @@
EXPECT_CALL(*mMockUidStatsCollector, collect()).Times(1);
EXPECT_CALL(*mMockProcStatCollector, collect()).Times(1);
+ EXPECT_CALL(*mMockDataProcessor, onCarWatchdogServiceRegistered()).Times(1);
EXPECT_CALL(*mMockDataProcessor,
onPeriodicCollection(_, SystemState::NORMAL_MODE, Eq(mMockUidStatsCollector),
Eq(mMockProcStatCollector), _))
@@ -1663,6 +1666,7 @@
EXPECT_CALL(*mMockUidStatsCollector, collect()).Times(1);
EXPECT_CALL(*mMockProcStatCollector, collect()).Times(1);
+ EXPECT_CALL(*mMockDataProcessor, onCarWatchdogServiceRegistered()).Times(1);
EXPECT_CALL(*mMockDataProcessor,
onPeriodicCollection(_, SystemState::NORMAL_MODE, Eq(mMockUidStatsCollector),
Eq(mMockProcStatCollector), _))
@@ -1762,7 +1766,9 @@
ASSERT_RESULT_OK(mLooperStub->pollCache());
ASSERT_NO_FATAL_FAILURE(verifyAndClearExpectations());
- ASSERT_EQ(actualResourceStats, expectedResourceStats);
+ ASSERT_EQ(actualResourceStats, expectedResourceStats)
+ << "Expected: " << toString(expectedResourceStats)
+ << "\nActual: " << toString(actualResourceStats);
}
} // namespace watchdog
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 2fea250..2da4fc3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -196,13 +196,6 @@
}
prebuilt_etc {
- name: "allowed_privapp_com.android.car.ui.paintbooth",
- sub_dir: "permissions",
- src: "com.android.car.ui.paintbooth.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
name: "allowed_privapp_com.android.car.provision",
system_ext_specific: true,
sub_dir: "permissions",
@@ -272,3 +265,10 @@
src: "com.android.car.multidisplay.passenger.xml",
filename_from_src: true,
}
+
+prebuilt_etc {
+ name: "allowed_privapp_com.google.android.car.multidisplaytest",
+ sub_dir: "permissions",
+ src: "com.google.android.car.multidisplaytest.xml",
+ filename_from_src: true,
+}
diff --git a/data/etc/com.android.car.bugreport.xml b/data/etc/com.android.car.bugreport.xml
index ec9128e..5a671d6 100644
--- a/data/etc/com.android.car.bugreport.xml
+++ b/data/etc/com.android.car.bugreport.xml
@@ -23,5 +23,7 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.car.permission.CAR_DRIVING_STATE"/>
<permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
+ <permission name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID"/>
+
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.car.developeroptions.xml b/data/etc/com.android.car.developeroptions.xml
index 0083fc5..1ac3688 100644
--- a/data/etc/com.android.car.developeroptions.xml
+++ b/data/etc/com.android.car.developeroptions.xml
@@ -37,6 +37,7 @@
<permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
<permission name="android.permission.MASTER_CLEAR"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
diff --git a/data/etc/com.android.car.shell.xml b/data/etc/com.android.car.shell.xml
index dafb606..0223665 100644
--- a/data/etc/com.android.car.shell.xml
+++ b/data/etc/com.android.car.shell.xml
@@ -24,6 +24,7 @@
<permission name="android.permission.LOCATION_BYPASS" />
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+ <permission name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID"/>
<permission name="android.car.permission.READ_DRIVER_MONITORING_SETTINGS"/>
<permission name="android.car.permission.CONTROL_DRIVER_MONITORING_SETTINGS"/>
<permission name="android.car.permission.READ_DRIVER_MONITORING_STATES"/>
@@ -32,6 +33,7 @@
<permission name="android.car.permission.CAR_DIAGNOSTICS"/>
<permission name="android.car.permission.CAR_DRIVING_STATE"/>
<permission name="android.car.permission.CAR_POWER"/>
+ <permission name="android.car.permission.CAR_TEST_SERVICE"/>
<permission name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
<permission name="android.car.permission.CONTROL_CAR_POWER_POLICY"/>
<permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
@@ -123,5 +125,6 @@
<permission name="android.car.permission.SET_CAR_VENDOR_CATEGORY_10"/>
<permission name="android.car.permission.MANAGE_OCCUPANT_CONNECTION" />
<permission name="android.car.permission.MANAGE_REMOTE_DEVICE" />
+ <permission name="android.car.permission.MANAGE_CAR_SYSTEM_UI" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.car.ui.paintbooth.xml b/data/etc/com.android.car.ui.paintbooth.xml
deleted file mode 100644
index 66a0147..0000000
--- a/data/etc/com.android.car.ui.paintbooth.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
-
- <privapp-permissions package="com.android.car.ui.paintbooth">
- <!-- For enabling/disabling, and getting list of RROs -->
- <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
- <!-- For showing the current active activity -->
- <permission name="android.permission.REAL_GET_TASKS"/>
- <!-- For getting list of RROs for current user -->
- <permission name="android.permission.MANAGE_USERS"/>
- <!-- For getting list of RROs for current user-->
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <!-- For ThemePlayground -->
- <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/com.google.android.car.kitchensink.xml b/data/etc/com.google.android.car.kitchensink.xml
index 37a82f3..c8298b2 100644
--- a/data/etc/com.google.android.car.kitchensink.xml
+++ b/data/etc/com.google.android.car.kitchensink.xml
@@ -34,6 +34,7 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MASTER_CLEAR"/>
<permission name="android.car.permission.ACCESS_MIRRORED_SURFACE"/>
+ <permission name="android.permission.DUMP"/>
<!-- use for CarServiceTest -->
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/data/etc/com.google.android.car.multidisplaytest.xml
similarity index 62%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to data/etc/com.google.android.car.multidisplaytest.xml
index 8ad0860..06f012d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/data/etc/com.google.android.car.multidisplaytest.xml
@@ -12,8 +12,12 @@
~ 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.
+ ~ limitations under the License
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+<permissions>
+ <privapp-permissions package="com.google.android.car.multidisplaytest">
+ <!-- Used for OccupantConnectionFragment -->
+ <permission name="android.car.permission.MANAGE_OCCUPANT_CONNECTION"/>
+ <permission name="android.car.permission.MANAGE_REMOTE_DEVICE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/packages/CarActivityResolver/AndroidManifest.xml b/packages/CarActivityResolver/AndroidManifest.xml
index e52f63e..9ffb5b1 100644
--- a/packages/CarActivityResolver/AndroidManifest.xml
+++ b/packages/CarActivityResolver/AndroidManifest.xml
@@ -47,7 +47,8 @@
android:name=".CarResolverActivity"
android:exported="true"
android:launchMode="singleInstance"
- android:noHistory="true">
+ android:noHistory="true"
+ android:excludeFromRecents="true">
<meta-data android:name="distractionOptimized" android:value="true"/>
</activity>
diff --git a/packages/CarDeveloperOptions/AndroidManifest.xml b/packages/CarDeveloperOptions/AndroidManifest.xml
index a6d23c7..4de39a8 100644
--- a/packages/CarDeveloperOptions/AndroidManifest.xml
+++ b/packages/CarDeveloperOptions/AndroidManifest.xml
@@ -314,6 +314,14 @@
<intent-filter tools:node="removeAll"/>
</activity>
+ <activity android:name="com.android.settings.Settings$FingerprintSettingsActivityV2"
+ android:enabled="false"
+ android:exported="false"
+ tools:node="merge"
+ tools:replace="android:exported">
+ <intent-filter tools:node="removeAll"/>
+ </activity>
+
<activity
android:name="com.android.settings.bluetooth.DevicePickerActivity"
android:enabled="false"
@@ -432,6 +440,15 @@
</activity>
<activity
+ android:name="com.android.settings.Settings$NavigationModeSettingsActivity"
+ android:enabled="false"
+ android:exported="false"
+ tools:node="merge"
+ tools:replace="android:exported">
+ <intent-filter tools:node="removeAll"/>
+ </activity>
+
+ <activity
android:name="com.android.settings.Settings$RegionalPreferencesActivity"
android:enabled="false"
android:exported="false"
@@ -711,6 +728,14 @@
<intent-filter tools:node="removeAll"/>
</activity>
+ <activity android:name="com.android.settings.Settings$UserAspectRatioAppListActivity"
+ android:enabled="false"
+ android:exported="false"
+ tools:node="merge"
+ tools:replace="android:exported">
+ <intent-filter tools:node="removeAll"/>
+ </activity>
+
<activity
android:name="com.android.settings.Settings$ManageDomainUrlsActivity"
android:enabled="false"
@@ -1843,7 +1868,7 @@
android:enabled="false"
android:exported="false"
tools:node="merge"
- tools:replace="android:exported">
+ tools:replace="android:exported, android:enabled">
<intent-filter tools:node="removeAll"/>
</activity>
@@ -1865,6 +1890,15 @@
<intent-filter tools:node="removeAll"/>
</activity>
+ <activity
+ android:name="com.android.settings.Settings$CommunalSettingsActivity"
+ android:enabled="false"
+ android:exported="false"
+ tools:node="merge"
+ tools:replace="android:exported">
+ <intent-filter tools:node="removeAll"/>
+ </activity>
+
<activity android:name="com.android.settings.dream.DreamSetupActivity"
android:enabled="false"
android:exported="false"
diff --git a/packages/CarDeveloperOptions/tests/unit/Android.bp b/packages/CarDeveloperOptions/tests/unit/Android.bp
index e4abaf7..1f35343 100644
--- a/packages/CarDeveloperOptions/tests/unit/Android.bp
+++ b/packages/CarDeveloperOptions/tests/unit/Android.bp
@@ -22,4 +22,6 @@
],
instrumentation_for: "CarDeveloperOptions",
+
+ test_suites: ["automotive-general-tests"],
}
diff --git a/packages/CarShell/AndroidManifest.xml b/packages/CarShell/AndroidManifest.xml
index 2314be7..d47a4cb 100644
--- a/packages/CarShell/AndroidManifest.xml
+++ b/packages/CarShell/AndroidManifest.xml
@@ -43,6 +43,8 @@
<uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" />
<!-- Permission required for 'adb shell cmd car_service silent-mode' -->
<uses-permission android:name="android.car.permission.CAR_POWER" />
+ <!-- Permission required for CTS test - OemCarServiceImplTest -->
+ <uses-permission android:name="android.car.permission.CAR_TEST_SERVICE"/>
<!-- Permission required for ATS tests - AtsCarTests#CarPowerManagerTest-->
<uses-permission android:name="android.car.permission.CONTROL_CAR_POWER_POLICY" />
<uses-permission android:name="android.car.permission.CONTROL_SHUTDOWN_PROCESS" />
@@ -67,6 +69,8 @@
<uses-permission android:name="android.car.permission.USE_CAR_TELEMETRY_SERVICE" />
<!-- Permission required for CTS test - LocationManagerFineTest -->
<uses-permission android:name="android.permission.LOCATION_BYPASS" />
+ <!-- Permissions required for CTS test - CarActivityManagerTest#getCarTaskViewController -->
+ <uses-permission android:name="android.car.permission.MANAGE_CAR_SYSTEM_UI" />
<!-- Permissions required for CTS test - CarPropertyManagerTest -->
<uses-permission android:name="android.car.permission.READ_DRIVER_MONITORING_SETTINGS"/>
<uses-permission android:name="android.car.permission.CONTROL_DRIVER_MONITORING_SETTINGS"/>
@@ -147,6 +151,7 @@
<!-- Permissions required for CTS test - CarPerformanceManagerTest -->
<uses-permission android:name="android.car.permission.MANAGE_THREAD_PRIORITY" />
<!-- Permissions required for CTS test - CarOccupantZoneManagerTest -->
+ <uses-permission android:name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID" />
<uses-permission android:name="android.car.permission.MANAGE_OCCUPANT_ZONE" />
<!-- Permissions required for CarOccupantConnectionManagerPermissionTest -->
<uses-permission android:name="android.car.permission.MANAGE_OCCUPANT_CONNECTION" />
diff --git a/service/Android.bp b/service/Android.bp
index cde1f0a..b0ea598 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -116,7 +116,8 @@
defaults: [ "carservice-updatable-min-java-defaults" ],
- min_sdk_version: "33",
+ // TODO(b/288271411): enable it when car mainline module is supported
+ // min_sdk_version: "33",
}
// Non-module build
diff --git a/service/jni/Android.bp b/service/jni/Android.bp
index 6af27b5..736fb1e 100644
--- a/service/jni/Android.bp
+++ b/service/jni/Android.bp
@@ -52,6 +52,7 @@
"-DLOG_TAG=\"CarServiceJNI\"",
"-Wall",
"-Werror",
+ "-Wthread-safety",
"-Wunused",
"-Wunreachable-code",
"-fvisibility=hidden",
diff --git a/service/jni/evs/EvsCallbackThread.cpp b/service/jni/evs/EvsCallbackThread.cpp
index f34cb15..2ccd1a5 100644
--- a/service/jni/evs/EvsCallbackThread.cpp
+++ b/service/jni/evs/EvsCallbackThread.cpp
@@ -50,6 +50,7 @@
Task task;
{
std::unique_lock<std::mutex> lock(mLock);
+ android::base::ScopedLockAssertion lock_assertion(mLock);
if (!mRunning) {
break;
}
@@ -80,13 +81,16 @@
}
}
- auto res = mVm->DetachCurrentThread();
- if (res != JNI_OK) {
- LOG(WARNING) << "Failed to be detached from the VM.";
- }
+ {
+ std::lock_guard lock(mLock);
+ auto res = mVm->DetachCurrentThread();
+ if (res != JNI_OK) {
+ LOG(WARNING) << "Failed to be detached from the VM.";
+ }
- if (!mTaskQueue.empty()) {
- LOG(WARNING) << mTaskQueue.size() << " tasks are ignored.";
+ if (!mTaskQueue.empty()) {
+ LOG(WARNING) << mTaskQueue.size() << " tasks are ignored.";
+ }
}
LOG(DEBUG) << "Exiting a callback handler thread.";
diff --git a/service/jni/evs/EvsCallbackThread.h b/service/jni/evs/EvsCallbackThread.h
index 4d90877b..0c0965e 100644
--- a/service/jni/evs/EvsCallbackThread.h
+++ b/service/jni/evs/EvsCallbackThread.h
@@ -42,7 +42,7 @@
std::queue<Task> mTaskQueue GUARDED_BY(mLock);
// Main loop to handle enqueued tasks continuously
- void threadLoop();
+ void threadLoop() EXCLUDES(mLock);
DISALLOW_COPY_AND_ASSIGN(EvsCallbackThread);
@@ -51,10 +51,10 @@
virtual ~EvsCallbackThread();
// Adds a new task to the queue
- void enqueue(const Task& task) ACQUIRE(mLock);
+ void enqueue(const Task& task) EXCLUDES(mLock);
// Stops a task handler thread
- void stop();
+ void stop() EXCLUDES(mLock);
};
} // namespace android::automotive::evs
diff --git a/service/jni/evs/EvsServiceContext.cpp b/service/jni/evs/EvsServiceContext.cpp
index eace3c6..62c30b1 100644
--- a/service/jni/evs/EvsServiceContext.cpp
+++ b/service/jni/evs/EvsServiceContext.cpp
@@ -15,6 +15,7 @@
*/
#include "EvsServiceContext.h"
+#include "NoOpEvsDisplay.h"
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidl/android/hardware/common/NativeHandle.h>
@@ -197,12 +198,12 @@
return true;
}
+ std::lock_guard lock(mLock);
if (!mServiceFactory || !mServiceFactory->init()) {
LOG(ERROR) << "Failed to connect to EVS service.";
return false;
}
- std::lock_guard lock(mLock);
auto deathRecipient = ::AIBinder_DeathRecipient_new(EvsServiceContext::onEvsServiceBinderDied);
auto status = ::ndk::ScopedAStatus::fromStatus(
mLinkUnlinkImpl->linkToDeath(mServiceFactory->getService()->asBinder().get(),
@@ -245,56 +246,73 @@
}
bool EvsServiceContext::openCamera(const char* id) {
- if (!isAvailable()) {
- LOG(ERROR) << "Has not connected to EVS service yet.";
- return false;
- }
-
- if (isCameraOpened()) {
- if (mCameraIdInUse == id) {
- LOG(DEBUG) << "Camera " << id << " is has opened already.";
- return true;
+ CameraDesc desc;
+ IEvsEnumerator* pService;
+ std::shared_ptr<IEvsCamera> cameraToClose;
+ {
+ std::lock_guard lock(mLock);
+ if (!isAvailableLocked()) {
+ LOG(ERROR) << "Has not connected to EVS service yet.";
+ return false;
}
- // Close a current camera device.
- if (!mServiceFactory->getService()->closeCamera(mCamera).isOk()) {
- LOG(WARNING) << "Failed to close a current camera device";
+ if (isCameraOpenedLocked()) {
+ if (mCameraIdInUse == id) {
+ LOG(DEBUG) << "Camera " << id << " is has opened already.";
+ return true;
+ }
+
+ cameraToClose = mCamera;
+ LOG(INFO) << "Current camera will be closed.";
+ }
+
+ auto it = std::find_if(mCameraList.begin(), mCameraList.end(),
+ [target = std::string(id)](const CameraDesc& desc) {
+ return target == desc.id;
+ });
+ if (it == mCameraList.end()) {
+ LOG(ERROR) << id << " is not available";
+ return false;
+ }
+
+ cameraToClose = mCamera;
+ desc = *it;
+ pService = mServiceFactory->getService();
+ if (pService == nullptr) {
+ LOG(ERROR) << "IEvsEnumerator is not available.";
+ return false;
}
}
- auto it = std::find_if(mCameraList.begin(), mCameraList.end(),
- [target = std::string(id)](const CameraDesc& desc) {
- return target == desc.id;
- });
- if (it == mCameraList.end()) {
- LOG(ERROR) << id << " is not available";
- return false;
+ // Close a current camera device.
+ if (cameraToClose && !pService->closeCamera(cameraToClose).isOk()) {
+ LOG(WARNING) << "Failed to close a current camera device";
}
std::vector<Stream> availableStreams;
+ pService->getStreamList(desc, &availableStreams);
+
+ Stream streamConfig = selectStreamConfiguration(availableStreams);
+ std::shared_ptr<IEvsCamera> camObj;
+ if (!pService->openCamera(id, streamConfig, &camObj).isOk() ||
+ !camObj) {
+ LOG(ERROR) << "Failed to open a camera " << id;
+ return false;
+ }
+
+ std::shared_ptr<StreamHandler> streamHandler =
+ ::ndk::SharedRefBase::make<StreamHandler>(camObj, this,
+ EvsServiceContext::kMaxNumFramesInFlight);
+ if (!streamHandler) {
+ LOG(ERROR) << "Failed to initialize a stream streamHandler.";
+ if (!pService->closeCamera(camObj).isOk()) {
+ LOG(ERROR) << "Failed to close a temporary camera device";
+ }
+ return false;
+ }
+
{
- std::lock_guard<std::mutex> lock(mLock);
- mServiceFactory->getService()->getStreamList(*it, &availableStreams);
-
- Stream streamConfig = selectStreamConfiguration(availableStreams);
- std::shared_ptr<IEvsCamera> camObj;
- if (!mServiceFactory->getService()->openCamera(id, streamConfig, &camObj).isOk() ||
- !camObj) {
- LOG(ERROR) << "Failed to open a camera " << id;
- return false;
- }
-
- std::shared_ptr<StreamHandler> streamHandler =
- ::ndk::SharedRefBase::make<StreamHandler>(camObj, this,
- EvsServiceContext::kMaxNumFramesInFlight);
- if (!streamHandler) {
- LOG(ERROR) << "Failed to initialize a stream streamHandler.";
- if (!mServiceFactory->getService()->closeCamera(camObj).isOk()) {
- LOG(ERROR) << "Failed to close a temporary camera device";
- }
- return false;
- }
-
+ std::lock_guard lock(mLock);
mCamera = std::move(camObj);
mStreamHandler = std::move(streamHandler);
mCameraIdInUse = id;
@@ -304,13 +322,13 @@
}
void EvsServiceContext::closeCamera() {
- if (!isAvailable() || !isCameraOpened()) {
+ std::lock_guard lock(mLock);
+ if (!isAvailableLocked() || !isCameraOpenedLocked()) {
LOG(DEBUG) << "Not connected to the Extended View System service or no camera has opened "
"yet; a request to close a camera is ignored.";
return;
}
- std::lock_guard lock(mLock);
if (!mServiceFactory->getService()->closeCamera(mCamera).isOk()) {
LOG(WARNING) << "Failed to close a current camera device.";
}
@@ -321,7 +339,8 @@
}
bool EvsServiceContext::startVideoStream() {
- if (!isAvailable() || !isCameraOpened()) {
+ std::lock_guard lock(mLock);
+ if (!isAvailableLocked() || !isCameraOpenedLocked()) {
LOG(ERROR)
<< "Not connected to the Extended View System service or no camera has opened yet.";
return JNI_FALSE;
@@ -331,15 +350,14 @@
}
void EvsServiceContext::stopVideoStream() {
- if (!isAvailable() || !isCameraOpened()) {
+ std::lock_guard lock(mLock);
+ if (!isAvailableLocked() || !isCameraOpenedLocked()) {
LOG(DEBUG) << "Not connected to the Extended View System service or no camera has opened "
"yet; a request to stop a video steram is ignored.";
return;
}
- if (!mStreamHandler->asyncStopStream()) {
- LOG(WARNING) << "Failed to stop a video stream. EVS service may die.";
- }
+ mStreamHandler->blockingStopStream();
}
void EvsServiceContext::acquireCameraAndDisplayLocked() {
@@ -358,6 +376,10 @@
LOG(WARNING) << "Failed to acquire the display ownership. "
<< "CarEvsManager may not be able to render "
<< "the contents on the screen.";
+
+ // We hold a no-op IEvsDisplay object to avoid attempting to open a
+ // display repeatedly.
+ mDisplay = ndk::SharedRefBase::make<NoOpEvsDisplay>();
return;
}
@@ -370,27 +392,25 @@
}
void EvsServiceContext::doneWithFrame(int bufferId) {
- {
- std::lock_guard<std::mutex> lock(mLock);
- if (!mStreamHandler) {
- LOG(DEBUG) << "A stream handler is not available.";
- return;
- }
+ std::lock_guard<std::mutex> lock(mLock);
+ if (!mStreamHandler) {
+ LOG(DEBUG) << "A stream handler is not available.";
+ return;
+ }
- auto it = mBufferRecords.find(bufferId);
- if (it == mBufferRecords.end()) {
- LOG(WARNING) << "Unknown buffer is requested to return.";
- return;
- }
+ auto it = mBufferRecords.find(bufferId);
+ if (it == mBufferRecords.end()) {
+ LOG(WARNING) << "Unknown buffer is requested to return.";
+ return;
+ }
- mBufferRecords.erase(it);
+ mBufferRecords.erase(it);
- // If this is the first frame since current video stream started, we'd claim
- // the exclusive ownership of the camera and the display and keep for the rest
- // of the lifespan.
- if (!mDisplay) {
- acquireCameraAndDisplayLocked();
- }
+ // If this is the first frame since current video stream started, we'd claim
+ // the exclusive ownership of the camera and the display and keep for the rest
+ // of the lifespan.
+ if (!mDisplay) {
+ acquireCameraAndDisplayLocked();
}
mStreamHandler->doneWithFrame(bufferId);
}
@@ -444,25 +464,29 @@
if (status != android::NO_ERROR) {
LOG(ERROR) << "Failed to create a raw hardware buffer from a native handle, "
<< "status = " << statusToString(status);
+ std::lock_guard lock(mLock);
mStreamHandler->doneWithFrame(bufferDesc.bufferId);
return false;
}
mCallbackThread.enqueue([ahwb, bufferId = bufferDesc.bufferId, this](JNIEnv* env) {
+ jobject hwBuffer;
{
std::lock_guard lock(mLock);
mBufferRecords.insert(bufferId);
+
+ // Forward AHardwareBuffer to the client
+ hwBuffer = AHardwareBuffer_toHardwareBuffer(env, ahwb);
+ if (!hwBuffer) {
+ LOG(WARNING) << "Failed to create HardwareBuffer from AHardwareBuffer.";
+ mStreamHandler->doneWithFrame(bufferId);
+ AHardwareBuffer_release(ahwb);
+ return;
+ }
}
- // Forward AHardwareBuffer to the client
- jobject hwBuffer = AHardwareBuffer_toHardwareBuffer(env, ahwb);
- if (!hwBuffer) {
- LOG(WARNING) << "Failed to create HardwareBuffer from AHardwareBuffer.";
- mStreamHandler->doneWithFrame(bufferId);
- } else {
- env->CallVoidMethod(mCarEvsServiceObj, mFrameHandlerMethodId, bufferId, hwBuffer);
- env->DeleteLocalRef(hwBuffer);
- }
+ env->CallVoidMethod(mCarEvsServiceObj, mFrameHandlerMethodId, bufferId, hwBuffer);
+ env->DeleteLocalRef(hwBuffer);
// We're done
AHardwareBuffer_release(ahwb);
diff --git a/service/jni/evs/EvsServiceContext.h b/service/jni/evs/EvsServiceContext.h
index 9539dc8..5f21d60 100644
--- a/service/jni/evs/EvsServiceContext.h
+++ b/service/jni/evs/EvsServiceContext.h
@@ -80,12 +80,12 @@
* service or to register a death recipient.
* true otherwise.
*/
- bool initialize(JNIEnv* env, jobject thiz) ACQUIRE(mLock);
+ bool initialize(JNIEnv* env, jobject thiz) EXCLUDES(mLock);
/*
* Deinitialize the service context and releases the resources.
*/
- void deinitialize() ACQUIRE(mLock);
+ void deinitialize() EXCLUDES(mLock);
/*
* Requests to open a target camera device.
@@ -95,43 +95,46 @@
* a camera device, or fails to initialize a stream handler;
* true otherwise.
*/
- bool openCamera(const char* id) ACQUIRE(mLock);
+ bool openCamera(const char* id) EXCLUDES(mLock);
/*
* Requests to close an active camera device.
*/
- void closeCamera();
+ void closeCamera() EXCLUDES(mLock);
/*
* Requests to start a video stream from a successfully opened camera device.
*/
- bool startVideoStream();
+ bool startVideoStream() EXCLUDES(mLock);
/*
* Requests to stop an active video stream.
*/
- void stopVideoStream();
+ void stopVideoStream() EXCLUDES(mLock);
/*
* Notifies that the client finishes with this buffer.
*
* @param frame a consumed frame buffer
*/
- void doneWithFrame(int bufferId);
+ void doneWithFrame(int bufferId) EXCLUDES(mLock);
/*
* Tells whether or not we're connected to the Extended View System service
*/
- bool isAvailable() ACQUIRE(mLock) {
+ bool isAvailable() EXCLUDES(mLock) {
std::lock_guard<std::mutex> lock(mLock);
+ return isAvailableLocked();
+ }
+
+ bool isAvailableLocked() REQUIRES(mLock) {
return mServiceFactory != nullptr && mServiceFactory->getService() != nullptr;
}
/*
* Tells whether or not a target camera device is opened
*/
- bool isCameraOpened() ACQUIRE(mLock) {
- std::lock_guard<std::mutex> lock(mLock);
+ bool isCameraOpenedLocked() REQUIRES(mLock) {
return mCamera != nullptr;
}
@@ -214,7 +217,7 @@
// Maximum number of frames CarEvsService can hold. This number has been
// chosen heuristically.
- static constexpr int kMaxNumFramesInFlight = 6;
+ static constexpr int kMaxNumFramesInFlight = 10;
// EVS service reserves a display ID 255 to allow the clients to open the main
// display exclusively.
diff --git a/service/jni/evs/NoOpEvsDisplay.h b/service/jni/evs/NoOpEvsDisplay.h
new file mode 100644
index 0000000..2493406
--- /dev/null
+++ b/service/jni/evs/NoOpEvsDisplay.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/automotive/evs/BnEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayState.h>
+
+namespace android::automotive::evs {
+
+namespace aidlevs = ::aidl::android::hardware::automotive::evs;
+
+class NoOpEvsDisplay final : public ::aidl::android::hardware::automotive::evs::BnEvsDisplay {
+public:
+ // Methods from ::aidl::android::hardware::automotive::evs::IEvsDisplay follow.
+ ndk::ScopedAStatus getDisplayInfo(aidlevs::DisplayDesc*) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus getDisplayState(aidlevs::DisplayState*) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus getTargetBuffer(aidlevs::BufferDesc*) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus returnTargetBufferForDisplay(const aidlevs::BufferDesc&) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus setDisplayState(aidlevs::DisplayState) override {
+ return ndk::ScopedAStatus::ok();
+ };
+
+ // Implementation details
+ virtual ~NoOpEvsDisplay() override = default;
+};
+
+} // namespace android::automotive::evs
diff --git a/service/jni/evs/StreamHandler.cpp b/service/jni/evs/StreamHandler.cpp
index 730c75c..1b8acad 100644
--- a/service/jni/evs/StreamHandler.cpp
+++ b/service/jni/evs/StreamHandler.cpp
@@ -34,6 +34,7 @@
using ::aidl::android::hardware::automotive::evs::IEvsCamera;
using ::aidl::android::hardware::common::NativeHandle;
using ::aidl::android::hardware::graphics::common::HardwareBuffer;
+using ::android::base::ScopedLockAssertion;
NativeHandle dupNativeHandle(const NativeHandle& handle, bool doDup) {
NativeHandle dup;
@@ -81,7 +82,8 @@
StreamHandler::StreamHandler(const std::shared_ptr<IEvsCamera>& camObj,
EvsServiceCallback* callback, int maxNumFramesInFlight) :
- mEvsCamera(camObj), mCallback(callback), mMaxNumFramesInFlight(maxNumFramesInFlight) {
+ mEvsCamera(camObj), mCallback(callback), mMaxNumFramesInFlightPerClient(maxNumFramesInFlight),
+ mNumClients(0) {
if (!camObj) {
LOG(ERROR) << "IEvsCamera is invalid.";
} else {
@@ -127,23 +129,47 @@
return false;
}
+ // This is the first client of this stream.
+ mNumClients = 1;
+
// Marks ourselves as running
mRunning = true;
+ } else {
+ // Increase a number of active clients and the max. number of frames in
+ // flight.
+ int desired = (++mNumClients) * mMaxNumFramesInFlightPerClient;
+ auto status = mEvsCamera->setMaxFramesInFlight(desired);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to adjust the maximum number of frames in flight for "
+ << mNumClients << " clients. Error: " << status.getServiceSpecificError();
+ // Decrease a number of clients back.
+ mNumClients -= 1;
+ return false;
+ }
}
return true;
}
/*
- * Requests to stop a video stream
+ * Requests to stop a video stream and waits for a confirmation
*/
-bool StreamHandler::asyncStopStream() {
- bool success = true;
-
- // This will result in STREAM_STOPPED event; the client may want to wait
- // this event to confirm the closure.
+void StreamHandler::blockingStopStream() {
{
- std::lock_guard<std::mutex> lock(mLock);
+ std::lock_guard lock(mLock);
+ if (!mRunning) {
+ // Nothing to do.
+ return;
+ }
+
+ if (mNumClients > 1) {
+ // Decrease a number of active clients and return.
+ --mNumClients;
+ return;
+
+ }
+
+ // Return all buffers currently held by us.
auto it = mReceivedBuffers.begin();
while (it != mReceivedBuffers.end()) {
// Packages a returned buffer and sends it back to the camera
@@ -153,7 +179,6 @@
if (!status.isOk()) {
LOG(WARNING) << "Failed to return a frame to EVS service; "
<< "this may leak the memory: " << status.getServiceSpecificError();
- success = false;
}
it = mReceivedBuffers.erase(it);
@@ -163,30 +188,21 @@
auto status = mEvsCamera->stopVideoStream();
if (!status.isOk()) {
LOG(WARNING) << "stopVideoStream() failed but ignored.";
- success = false;
}
- return success;
-}
-
-/*
- * Requests to stop a video stream and waits for a confirmation
- */
-void StreamHandler::blockingStopStream() {
- if (!asyncStopStream()) {
- // EVS service may die so no stream-stop event occurs.
- std::lock_guard<std::mutex> lock(mLock);
- mRunning = false;
- return;
- }
-
- // Waits until the stream has actually stopped
- std::unique_lock<std::mutex> lock(mLock);
- while (mRunning) {
- if (!mCondition.wait_for(lock, 1s, [this]() { return !mRunning; })) {
- LOG(WARNING) << "STREAM_STOPPED event timer expired. EVS service may die.";
- break;
+ // Now, we are waiting for the ack from EvsManager service.
+ {
+ std::unique_lock<std::mutex> lock(mLock);
+ ScopedLockAssertion lock_assertion(mLock);
+ while (mRunning) {
+ if (!mCondition.wait_for(lock, 1s, [this]() REQUIRES(mLock) { return !mRunning; })) {
+ LOG(WARNING) << "STREAM_STOPPED event timer expired. EVS service may die.";
+ break;
+ }
}
+
+ // Decrease a number of active clients.
+ --mNumClients;
}
}
@@ -235,7 +251,7 @@
numBuffersInUse = mReceivedBuffers.size();
}
- if (numBuffersInUse >= mMaxNumFramesInFlight) {
+ if (numBuffersInUse >= mMaxNumFramesInFlightPerClient) {
// We're holding more than what allowed; returns this buffer
// immediately.
doneWithFrame(bufferToUse);
@@ -285,6 +301,7 @@
[[fallthrough]];
case EvsEventType::TIMEOUT:
LOG(INFO) << "Event 0x" << std::hex << static_cast<int32_t>(event.aType)
+ << " from " << (event.deviceId.empty() ? "Unknown" : event.deviceId)
<< " is received but ignored";
break;
default:
diff --git a/service/jni/evs/StreamHandler.h b/service/jni/evs/StreamHandler.h
index a8c2a3a..0d190f0 100644
--- a/service/jni/evs/StreamHandler.h
+++ b/service/jni/evs/StreamHandler.h
@@ -44,20 +44,21 @@
EvsServiceCallback* callback, int maxNumFramesInFlight);
virtual ~StreamHandler();
void shutdown();
- bool startStream();
- bool asyncStopStream();
- void blockingStopStream();
- bool isRunning();
- void doneWithFrame(const ::aidl::android::hardware::automotive::evs::BufferDesc& buffer);
- void doneWithFrame(int bufferId);
+ bool startStream() EXCLUDES(mLock);
+ void blockingStopStream() EXCLUDES(mLock);
+ bool isRunning() EXCLUDES(mLock);
+ void doneWithFrame(const ::aidl::android::hardware::automotive::evs::BufferDesc& buffer)
+ EXCLUDES(mLock);
+ void doneWithFrame(int bufferId) EXCLUDES(mLock);
private:
// Implementation for ::aidl::android::hardware::automotive::evs::IEvsCameraStream
::ndk::ScopedAStatus deliverFrame(
const std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>& buffer)
- override;
+ override EXCLUDES(mLock);
::ndk::ScopedAStatus notify(
- const ::aidl::android::hardware::automotive::evs::EvsEventDesc& event) override;
+ const ::aidl::android::hardware::automotive::evs::EvsEventDesc& event) override
+ EXCLUDES(mLock);
// Values initialized as startup
std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera> mEvsCamera;
@@ -74,7 +75,10 @@
std::list<::aidl::android::hardware::automotive::evs::BufferDesc> mReceivedBuffers
GUARDED_BY(mLock);
- int mMaxNumFramesInFlight;
+ int mMaxNumFramesInFlightPerClient;
+
+ // Track number of active streaming clients.
+ int mNumClients GUARDED_BY(mLock);
};
} // namespace android::automotive::evs
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c39d6cf..0bafb9b 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -284,12 +284,14 @@
userPostUnlocked - start service later after user unlocked. This is used when the
service is not urgent and can wait to start.
The default value is 'userUnlocked'
+ maxRetries: the maximum number of attempts to rebind/restart a disconnected service.
+ Retries start with 4 second initial delay, being doubled after each failed attempt.
+ The default value is 6.
If the service bound/started for foreground user it will be unbound/stopped when user
is no longer foreground.
-->
<string-array translatable="false" name="config_earlyStartupServices">
- <item>com.android.car.messenger/.MessengerService#bind=startForeground,user=foreground,trigger=userUnlocked</item>
</string-array>
<!-- The consent activity that must be shown for every unknown mobile device before projection
@@ -509,11 +511,35 @@
The default duration is 1 min (= 1 * 60 * 1000). -->
<integer name="config_noUserScreenOffTimeout">60000</integer>
- <!-- A name of a camera device that provides the rearview through EVS service -->
+ <!--
+ CarEvsService configurations that associate camera devices with service types.
+
+ Some examples are:
+ <item>
+ serviceType=REARVIEW,cameraId=/dev/video0,activityName=com.android.car/com.google.android.car.evs.CarEvsCameraPreviewActivity
+ </item>
+ <item>serviceType=FRONTVIEW,cameraId=/dev/video1</item>
+
+ serviceType: One of CarEvsService types to be enabled. Please use "*" part from
+ CarEvsManager.SERVICE_TYPE_* such as REARVIEW and FRONTVIEW.
+ cameraId: A camera identifier that will be associated with a given service type.
+ This should be exclusive to a single service type.
+ activityName: A component name of an activity to be launched by the system events.
+ -->
+ <string-array name="config_carEvsService" translatable="false">
+ </string-array>
+
+ <!--
+ A name of a camera device that provides the rearview through EVS service.
+ This configuration becomes effective if and only if config_carEvsService is empty.
+ -->
<string name="config_evsRearviewCameraId" translatable="false">/dev/video10</string>
- <!-- The camera Activity name for EVS, if defined, the Activity will be launched by
- CarEvsService. -->
+ <!--
+ The camera Activity name for EVS, if defined, the Activity will be launched by
+ CarEvsService. This configuration is effective if and only if config_carEvsService
+ is empty.
+ -->
<string name="config_evsCameraActivity" translatable="false"></string>
<!-- A configuration flag to adjust Wifi for suspend. -->
diff --git a/service/res/values/overlayable.xml b/service/res/values/overlayable.xml
index 01aefe4..0d240e2 100644
--- a/service/res/values/overlayable.xml
+++ b/service/res/values/overlayable.xml
@@ -108,6 +108,7 @@
<item type="integer" name="config_noUserScreenOffTimeout"/>
<item type="string" name="config_evsRearviewCameraId" translatable="false"/>
<item type="string" name="config_evsCameraActivity" translatable="false"/>
+ <item type="array" name="config_carEvsService" translatable="false" />
<item type="bool" name="config_wifiAdjustmentForSuspend"/>
<item type="bool" name="config_enableCarLocationServiceGnssControlsForPowerManagement"/>
<item type="bool" name="config_preventTemplatedAppsFromShowingDialog"/>
diff --git a/service/src/com/android/car/CarMediaService.java b/service/src/com/android/car/CarMediaService.java
index a9f0ca6..0204abf 100644
--- a/service/src/com/android/car/CarMediaService.java
+++ b/service/src/com/android/car/CarMediaService.java
@@ -18,6 +18,7 @@
import static android.car.CarOccupantZoneManager.INVALID_USER_ID;
import static android.car.CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
import static android.car.builtin.os.UserManagerHelper.getMaxRunningUsers;
+import static android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT;
import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE;
import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK;
import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE;
@@ -443,6 +444,7 @@
maybeInitSharedPrefs(userId);
+ ComponentName playbackSource;
synchronized (mLock) {
UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId);
if (userMediaContext.mContext != null) {
@@ -459,9 +461,11 @@
defaultMediaSource;
userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] =
defaultMediaSource;
+ playbackSource = defaultMediaSource;
} else {
+ playbackSource = getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK, userId);
userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] =
- getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK, userId);
+ playbackSource;
userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] =
getLastMediaSource(MEDIA_SOURCE_MODE_BROWSE, userId);
}
@@ -472,7 +476,13 @@
notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK, userId);
notifyListeners(MEDIA_SOURCE_MODE_BROWSE, userId);
- startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig, userId), userId);
+ try {
+ startMediaConnectorService(playbackSource,
+ shouldStartPlayback(mPlayOnBootConfig, userId), userId);
+ } catch (Exception e) {
+ Slogf.e(TAG, e, "Failed to startMediaConnectorService. Source:%s user:%d",
+ playbackSource, userId);
+ }
}
@GuardedBy("mLock")
@@ -515,7 +525,8 @@
* resume playback using the MediaSession obtained via the media browser connection, which
* is more reliable than using active MediaSessions from MediaSessionManager.
*/
- private void startMediaConnectorService(boolean startPlayback, @UserIdInt int userId) {
+ private void startMediaConnectorService(@Nullable ComponentName playbackMediaSource,
+ boolean startPlayback, @UserIdInt int userId) {
synchronized (mLock) {
Context userContext = getOrCreateUserMediaPlayContextLocked(userId).mContext;
if (userContext == null) {
@@ -528,7 +539,13 @@
serviceStart.setPackage(
mContext.getResources().getString(R.string.serviceMediaConnection));
serviceStart.putExtra(EXTRA_AUTOPLAY, startPlayback);
- userContext.startForegroundService(serviceStart);
+ if (playbackMediaSource != null) {
+ serviceStart.putExtra(EXTRA_MEDIA_COMPONENT, playbackMediaSource.flattenToString());
+ }
+
+ ComponentName result = userContext.startForegroundService(serviceStart);
+ Slogf.i(TAG, "startMediaConnectorService user: %d, source: %s, result: %s", userId,
+ playbackMediaSource, result);
}
}
@@ -1247,7 +1264,7 @@
getRemovedMediaSourceComponentsForUser(userId)[MEDIA_SOURCE_MODE_PLAYBACK] = null;
}
notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK, userId);
- startMediaConnectorService(
+ startMediaConnectorService(playbackMediaSource,
shouldStartPlayback(mPlayOnMediaSourceChangedConfig, userId), userId);
} else {
Slogf.i(TAG, "Media source is null for user %d, skip starting media "
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index d9f89db..b16df20 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -45,6 +45,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.Pair;
@@ -55,6 +56,7 @@
import com.android.car.internal.property.AsyncPropertyServiceRequest;
import com.android.car.internal.property.AsyncPropertyServiceRequestList;
import com.android.car.internal.property.CarPropertyConfigList;
+import com.android.car.internal.property.CarPropertyHelper;
import com.android.car.internal.property.IAsyncPropertyResultCallback;
import com.android.car.internal.property.InputSanitizationUtils;
import com.android.car.internal.util.ArrayUtils;
@@ -393,11 +395,35 @@
private void getAndDispatchPropertyInitValue(CarPropertyConfig carPropertyConfig,
Client client) {
List<CarPropertyEvent> events = new ArrayList<>();
+ int propertyId = carPropertyConfig.getPropertyId();
for (int areaId : carPropertyConfig.getAreaIds()) {
- CarPropertyValue value = getPropertySafe(carPropertyConfig.getPropertyId(), areaId);
- if (value != null) {
+ CarPropertyValue carPropertyValue = null;
+ try {
+ carPropertyValue = getProperty(propertyId, areaId);
+ } catch (ServiceSpecificException e) {
+ Slogf.w("Get initial carPropertyValue for registerCallback failed - property ID: "
+ + "%s, area ID $s, exception: %s",
+ VehiclePropertyIds.toString(propertyId), Integer.toHexString(areaId), e);
+ int errorCode = CarPropertyHelper.getVhalSystemErrorCode(e.errorCode);
+ long timestampNanos = SystemClock.elapsedRealtimeNanos();
+ Object defaultValue = CarPropertyHelper.getDefaultValue(
+ carPropertyConfig.getPropertyType());
+ if (CarPropertyHelper.isNotAvailableVehicleHalStatusCode(errorCode)) {
+ carPropertyValue = new CarPropertyValue<>(propertyId, areaId,
+ CarPropertyValue.STATUS_UNAVAILABLE, timestampNanos, defaultValue);
+ } else {
+ carPropertyValue = new CarPropertyValue<>(propertyId, areaId,
+ CarPropertyValue.STATUS_ERROR, timestampNanos, defaultValue);
+ }
+ } catch (Exception e) {
+ // Do nothing.
+ Slogf.e("Get initial carPropertyValue for registerCallback failed - property ID: "
+ + "%s, area ID $s, exception: %s",
+ VehiclePropertyIds.toString(propertyId), Integer.toHexString(areaId), e);
+ }
+ if (carPropertyValue != null) {
CarPropertyEvent event = new CarPropertyEvent(
- CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
+ CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, carPropertyValue);
events.add(event);
}
}
diff --git a/service/src/com/android/car/CarServiceUtils.java b/service/src/com/android/car/CarServiceUtils.java
index 31b1997..3d96d22 100644
--- a/service/src/com/android/car/CarServiceUtils.java
+++ b/service/src/com/android/car/CarServiceUtils.java
@@ -39,12 +39,14 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.hardware.automotive.vehicle.SubscribeOptions;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
@@ -739,6 +741,7 @@
if (!isPlatformVersionAtLeastU()) {
return false;
}
+ if (DBG) Slogf.d(TAG, "Start SystemUI for user: %d", userId);
Preconditions.checkArgument(userId != UserHandle.SYSTEM.getIdentifier(),
"Cannot start SystemUI for the system user");
Preconditions.checkArgument(userId != ActivityManager.getCurrentUser(),
@@ -748,8 +751,8 @@
ComponentName sysuiComponent = PackageManagerHelper.getSystemUiServiceComponent(context);
Intent sysUIIntent = new Intent().setComponent(sysuiComponent);
try {
- context.createContextAsUser(UserHandle.of(userId), /* flags= */ 0).startService(
- sysUIIntent);
+ context.bindServiceAsUser(sysUIIntent, sEmptyServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.of(userId));
return true;
} catch (Exception e) {
Slogf.w(TAG, e, "Cannot start SysUI component %s for user %d", sysuiComponent,
@@ -758,6 +761,15 @@
}
}
+ // The callbacks are not called actually, because SystemUI returns null for IBinder.
+ private static final ServiceConnection sEmptyServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {}
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+
/**
* Stops the SystemUI component for a particular user - this function should not be called
* for the system user.
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index 2121450..ed305cc 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -259,6 +259,11 @@
private static final String COMMAND_SET_REARVIEW_CAMERA_ID = "set-rearview-camera-id";
private static final String COMMAND_GET_REARVIEW_CAMERA_ID = "get-rearview-camera-id";
+ private static final String COMMAND_SET_CAMERA_ID = "set-camera-id";
+ private static final String COMMAND_GET_CAMERA_ID = "get-camera-id";
+ private static final String COMMAND_ENABLE_CAMERA_SERVICE_TYPE = "enable-camera-service-type";
+ private static final String COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED =
+ "check-camera-service-type-enabled";
private static final String COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE =
"watchdog-control-package-killable-state";
@@ -291,6 +296,8 @@
private static final String COMMAND_GET_DISPLAY_BY_USER = "get-display-by-user";
private static final String COMMAND_GET_USER_BY_DISPLAY = "get-user-by-display";
+ private static final String COMMAND_GENERATE_TEST_VENDOR_CONFIGS = "gen-test-vendor-configs";
+ private static final String COMMAND_RESTORE_TEST_VENDOR_CONFIGS = "restore-vendor-configs";
private static final String[] CREATE_OR_MANAGE_USERS_PERMISSIONS = new String[] {
@@ -845,7 +852,7 @@
pw.printf("\t%s [%s] [%s]\n", COMMAND_POWER_OFF, PARAM_SKIP_GARAGEMODE, PARAM_REBOOT);
pw.println("\t Powers off the car.");
- pw.printf("\t%s <CAMERA_ID>\n", COMMAND_SET_REARVIEW_CAMERA_ID);
+ pw.printf("\t%s <REARVIEW_CAMERA_ID>\n", COMMAND_SET_REARVIEW_CAMERA_ID);
pw.println("\t Configures a target camera device CarEvsService to use.");
pw.println("\t If CAMEAR_ID is \"default\", this command will configure CarEvsService ");
pw.println("\t to use its default camera device.");
@@ -854,6 +861,29 @@
pw.println("\t Gets the name of the camera device CarEvsService is using for " +
"the rearview.");
+ pw.printf("\t%s <SERVICE_TYPE> <CAMERA_ID>\n", COMMAND_SET_CAMERA_ID);
+ pw.println("\t Configures a target camera device CarEvsService will use for a specified ");
+ pw.println("\t service type.");
+ pw.println("\t Possible SERVICE_TYPEs are REARVIEW, FRONTVIEW, LEFTVIEW, RIGHTVIEW, ");
+ pw.println("\t DRIVERVIEW, FRONT_PASSENGERSVIEW, REAR_PASSENGERSVIEW, or USER_DEFINED");
+ pw.println("\t (* of CarEvsManager.SERVICE_TYPE_* to specify a service type).");
+
+ pw.printf("\t%s <SERVICE_TYPE>\n", COMMAND_GET_CAMERA_ID);
+ pw.println("\t Gets the name of the camera device that is assigned to a specified ");
+ pw.println("\t service type.");
+ pw.println("\t Possible SERVICE_TYPEs are REARVIEW, FRONTVIEW, LEFTVIEW, RIGHTVIEW, ");
+ pw.println("\t DRIVERVIEW, FRONT_PASSENGERSVIEW, REAR_PASSENGERSVIEW, or USER_DEFINED");
+ pw.println("\t (* of CarEvsManager.SERVICE_TYPE_* to specify a service type).");
+
+ pw.printf("\t%s <SERVICE_TYPE> <CAMERA_ID>\n", COMMAND_ENABLE_CAMERA_SERVICE_TYPE);
+ pw.println("\t Enables a specified service type with a camera associated with a given ");
+ pw.println("\t camera id.");
+ pw.println("\t Use * of CarEvsManager.SERVICE_TYPE_* to specify a service type.");
+
+ pw.printf("\t%s <SERVICE_TYPE>\n", COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED);
+ pw.println("\t Checks whether or not a given service type is enabled.");
+ pw.println("\t Use * of CarEvsManager.SERVICE_TYPE_* to specify a service type.");
+
pw.printf("\t%s true|false <PACKAGE_NAME>\n",
COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE);
pw.println("\t Marks PACKAGE_NAME as killable or not killable on resource overuse ");
@@ -1193,6 +1223,24 @@
mHal.dumpPropertyValueByCommand(writer, Integer.decode(propId),
Integer.decode(areaId));
break;
+ case COMMAND_GENERATE_TEST_VENDOR_CONFIGS:
+ try (CarTestService.NativePipe pipe = CarTestService.NativePipe.newPipe()) {
+ mHal.dumpVhal(pipe.getFileDescriptor(), List.of("--genTestVendorConfigs"));
+ writer.print(pipe.getOutput(DEFAULT_HAL_TIMEOUT_MS));
+ } catch (Exception e) {
+ Slogf.w(TAG, "dumpVhal --genTestVendorConfigs Failed", e);
+ return showInvalidArguments(writer);
+ }
+ break;
+ case COMMAND_RESTORE_TEST_VENDOR_CONFIGS:
+ try (CarTestService.NativePipe pipe = CarTestService.NativePipe.newPipe()) {
+ mHal.dumpVhal(pipe.getFileDescriptor(), List.of("--restoreVendorConfigs"));
+ writer.print(pipe.getOutput(DEFAULT_HAL_TIMEOUT_MS));
+ } catch (Exception e) {
+ Slogf.w(TAG, "dumpVhal --genTestVendorConfigs Failed", e);
+ return showInvalidArguments(writer);
+ }
+ break;
case COMMAND_SET_PROPERTY_VALUE:
runSetVehiclePropertyValue(args, writer);
break;
@@ -1384,6 +1432,18 @@
case COMMAND_GET_REARVIEW_CAMERA_ID:
getRearviewCameraId(writer);
break;
+ case COMMAND_SET_CAMERA_ID:
+ setCameraId(args, writer);
+ break;
+ case COMMAND_GET_CAMERA_ID:
+ getCameraId(args, writer);
+ break;
+ case COMMAND_ENABLE_CAMERA_SERVICE_TYPE:
+ enableCameraServiceType(args, writer);
+ break;
+ case COMMAND_CHECK_CAMERA_SERVICE_TYPE_ENABLED:
+ checkCameraServiceTypeEnabled(args, writer);
+ break;
case COMMAND_WATCHDOG_CONTROL_PACKAGE_KILLABLE_STATE:
controlWatchdogPackageKillableState(args, writer);
break;
@@ -3142,6 +3202,35 @@
}
}
+ private void setCameraId(String[] args, IndentingPrintWriter writer) {
+ if (args.length != 3) {
+ showInvalidArguments(writer);
+ return;
+ }
+
+ if (!mCarEvsService.setCameraIdFromCommand(args[1], args[2])) {
+ writer.printf("Failed to set CarEvsService camera device id for %s.", args[1]);
+ } else {
+ writer.printf("CarEvsService is set to use %s for %s.\n", args[2], args[1]);
+ }
+ }
+
+ private void enableCameraServiceType(String[] args, IndentingPrintWriter writer) {
+ if (args.length != 3) {
+ showInvalidArguments(writer);
+ return;
+ }
+
+ if (!mCarEvsService.enableServiceTypeFromCommand(args[1], args[2])) {
+ writer.printf("Failed to enable %s with a camera %s.\n",
+ args[1], args[2]);
+ return;
+ }
+
+ writer.printf("%s is successfully enabled and set to use a camera %s.\n",
+ args[1], args[2]);
+ }
+
private void setDrivingSafetyRegion(String[] args, IndentingPrintWriter writer) {
if (args.length != 1 && args.length != 2) {
showInvalidArguments(writer);
@@ -3158,6 +3247,31 @@
mCarEvsService.getRearviewCameraIdFromCommand());
}
+ private void getCameraId(String[] args, IndentingPrintWriter writer) {
+ if (args.length != 2) {
+ showInvalidArguments(writer);
+ return;
+ }
+
+ writer.printf("CarEvsService is using %s for %s.\n",
+ mCarEvsService.getCameraIdFromCommand(args[1]), args[1]);
+ }
+
+ private void checkCameraServiceTypeEnabled(String[] args, IndentingPrintWriter writer) {
+ if (args.length != 2) {
+ showInvalidArguments(writer);
+ return;
+ }
+
+ if (!mCarEvsService.isServiceTypeEnabledFromCommand(args[1])) {
+ writer.printf("%s is not enabled.\n", args[1]);
+ return;
+ }
+
+ String cameraId = mCarEvsService.getCameraIdFromCommand(args[1]);
+ writer.printf("%s is enabled and set to use %s.\n", args[1], cameraId);
+ }
+
private void controlWatchdogPackageKillableState(String[] args, IndentingPrintWriter writer) {
if (args.length != 3) {
showInvalidArguments(writer);
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index 90ceb5f..5f1c2c6 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -178,7 +178,7 @@
}
// A helper class to create a native pipe used in debug functions.
- private static class NativePipe implements AutoCloseable {
+ /* package */ static class NativePipe implements AutoCloseable {
private final ParcelFileDescriptor mWriter;
private final ParcelFileDescriptor mReader;
private Thread mThread;
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 22594cc..d3e2993 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -288,7 +288,8 @@
int maxRunningUsers = UserManagerHelper.getMaxRunningUsers(serviceContext);
mCarUserService = constructWithTrace(t, CarUserService.class,
() -> new CarUserService(serviceContext, mHal.getUserHal(), userManager,
- maxRunningUsers, mCarUXRestrictionsService, mCarPackageManagerService),
+ maxRunningUsers, mCarUXRestrictionsService, mCarPackageManagerService,
+ mCarOccupantZoneService),
allServices);
}
if (mDoPriorityInitInConstruction) {
diff --git a/service/src/com/android/car/admin/CarDevicePolicyService.java b/service/src/com/android/car/admin/CarDevicePolicyService.java
index 56cc098..b2562d4 100644
--- a/service/src/com/android/car/admin/CarDevicePolicyService.java
+++ b/service/src/com/android/car/admin/CarDevicePolicyService.java
@@ -151,6 +151,8 @@
@Override
public void createUser(@Nullable String name, @CarDevicePolicyManager.UserType int type,
ResultCallbackImpl<UserCreationResult> callback) {
+ UserCreationRequest.Builder userCreationRequestBuilder =
+ new UserCreationRequest.Builder().setName(name);
int userInfoFlags = 0;
String userType = UserManager.USER_TYPE_FULL_SECONDARY;
switch(type) {
@@ -158,9 +160,11 @@
break;
case CarDevicePolicyManager.USER_TYPE_ADMIN:
userInfoFlags = UserManagerHelper.FLAG_ADMIN;
+ userCreationRequestBuilder.setAdmin();
break;
case CarDevicePolicyManager.USER_TYPE_GUEST:
userType = UserManager.USER_TYPE_FULL_GUEST;
+ userCreationRequestBuilder.setGuest();
break;
default:
Slogf.d(TAG, "createUser(): invalid userType (%s) / flags (%08x) "
@@ -173,7 +177,7 @@
Slogf.d(TAG, "calling createUser(%s, %s, %d, %d)",
UserHelperLite.safeName(name), userType, userInfoFlags, HAL_TIMEOUT_MS);
- mCarUserService.createUser(new UserCreationRequest.Builder().build(), HAL_TIMEOUT_MS,
+ mCarUserService.createUser(userCreationRequestBuilder.build(), HAL_TIMEOUT_MS,
callback);
}
diff --git a/service/src/com/android/car/am/CarActivityService.java b/service/src/com/android/car/am/CarActivityService.java
index 2136846..9af58cf 100644
--- a/service/src/com/android/car/am/CarActivityService.java
+++ b/service/src/com/android/car/am/CarActivityService.java
@@ -38,7 +38,6 @@
import android.car.app.ICarSystemUIProxyCallback;
import android.car.builtin.app.ActivityManagerHelper;
import android.car.builtin.app.TaskInfoHelper;
-import android.car.builtin.content.ContextHelper;
import android.car.builtin.os.UserManagerHelper;
import android.car.builtin.util.Slogf;
import android.car.builtin.view.SurfaceControlHelper;
@@ -606,8 +605,8 @@
/**
* Attempts to restart a task.
*
- * <p>Restarts a task by sending an empty intent with flag
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} to its root activity. If the task does not exist, do
+ * <p>Restarts a task by removing the task and sending an empty intent with flag
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK} to its root activity. If the task does not exist, do
* nothing.
*
* @param taskId id of task to be restarted.
@@ -623,10 +622,14 @@
}
Intent intent = (Intent) task.baseIntent.clone();
- // Clear the task the root activity is running in and start it in a new task.
- // Effectively restart root activity.
- intent.addFlags(
- Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Remove the task the root activity is running in and start it in a new task.
+ // This effectively leads to restarting of the root activity and removal all the other
+ // activities in the task.
+ // FLAG_ACTIVITY_CLEAR_TASK was being used earlier, but it has the side effect where the
+ // last activity in the existing task is visible for a moment until the root activity is
+ // started again.
+ ActivityManagerHelper.removeTask(taskId);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
int userId = TaskInfoHelper.getUserId(task);
if (Slogf.isLoggable(CarLog.TAG_AM, Log.INFO)) {
@@ -668,8 +671,10 @@
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(displayId);
- ContextHelper.startActivityAsUser(mContext, newActivityIntent, options.toBundle(),
- UserHandle.of(TaskInfoHelper.getUserId(currentTask)));
+ // Starts ABA as User 0 consistenly since the target apps can be any users (User 0 -
+ // UserPicker, Driver/Passegners - general NDO apps) and launching ABA as passengers
+ // have some issue (b/294447050).
+ mContext.startActivity(newActivityIntent, options.toBundle());
// Now make stack with new activity focused.
findTaskAndGrantFocus(newActivityIntent.getComponent());
}
diff --git a/service/src/com/android/car/am/FixedActivityService.java b/service/src/com/android/car/am/FixedActivityService.java
index 49d1402..dfac064 100644
--- a/service/src/com/android/car/am/FixedActivityService.java
+++ b/service/src/com/android/car/am/FixedActivityService.java
@@ -82,7 +82,7 @@
*/
public final class FixedActivityService implements CarServiceBase {
- private static final boolean DBG = false;
+ private static final boolean DBG = Slogf.isLoggable(TAG_AM, Log.DEBUG);
private static final long RECHECK_INTERVAL_MS = 500;
private static final int MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY = 5;
@@ -147,7 +147,7 @@
if (!isEventOfType(TAG_AM, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) {
return;
}
- if (Slogf.isLoggable(TAG_AM, Log.DEBUG)) {
+ if (DBG) {
Slogf.d(TAG_AM, "onEvent(" + event + ")");
}
@@ -363,19 +363,12 @@
}
long now = SystemClock.elapsedRealtime();
synchronized (mLock) {
- if (mRunningActivities.size() == 0) {
- // it must have been stopped.
- if (DBG) {
- Slogf.i(TAG_AM, "empty activity list", new RuntimeException());
- }
- return false;
- }
for (int i = mRunningActivities.size() - 1; i >= 0; i--) {
int displayIdForActivity = mRunningActivities.keyAt(i);
Display display = mDm.getDisplay(displayIdForActivity);
- if (display == null) {
- Slogf.e(TAG_AM, "Stop fixed activity for non-available display"
- + displayIdForActivity);
+ if (display == null || display.getState() != Display.STATE_ON) {
+ Slogf.e(TAG_AM, "Stop fixed activity for %s display%d",
+ display == null ? "non-available" : "non-active", displayIdForActivity);
mRunningActivities.removeAt(i);
continue;
}
@@ -403,6 +396,15 @@
}
mRunningActivities.removeAt(i);
}
+ if (mRunningActivities.size() == 0) {
+ // it must have been stopped.
+ Slogf.i(TAG_AM, "empty activity list");
+ stopMonitoringEvents();
+ return false;
+ }
+ if (DBG) {
+ Slogf.i(TAG_AM, "Visible Tasks: %d", infos.size());
+ }
for (int i = 0, size = infos.size(); i < size; ++i) {
TaskInfo taskInfo = infos.get(i);
int taskDisplayId = TaskInfoHelper.getDisplayId(taskInfo);
@@ -410,6 +412,10 @@
if (activityInfo == null) {
continue;
}
+ if (DBG) {
+ Slogf.i(TAG_AM, "Task#%d: U%d top=%s orig=%s", i, activityInfo.userId,
+ taskInfo.topActivity, taskInfo.origActivity);
+ }
int taskUserId = TaskInfoHelper.getUserId(taskInfo);
ComponentName expectedTopActivity = activityInfo.intent.getComponent();
if ((expectedTopActivity.equals(taskInfo.topActivity)
diff --git a/service/src/com/android/car/audio/CarAudioContext.java b/service/src/com/android/car/audio/CarAudioContext.java
index 72b9c4e..36441ea 100644
--- a/service/src/com/android/car/audio/CarAudioContext.java
+++ b/service/src/com/android/car/audio/CarAudioContext.java
@@ -445,6 +445,20 @@
}
}
+ static @AudioContext int getLegacyContextFromInfo(CarAudioContextInfo carAudioContextInfo) {
+ AudioAttributes[] attributes = carAudioContextInfo.getAudioAttributes();
+ for (int index = 0; index < attributes.length; index++) {
+ AudioAttributesWrapper wrapper =
+ new AudioAttributesWrapper(attributes[index]);
+ int context = AUDIO_ATTRIBUTE_TO_CONTEXT.getOrDefault(wrapper, INVALID);
+ if (isInvalidContextId(context)) {
+ continue;
+ }
+ return context;
+ }
+ return INVALID;
+ }
+
static List<AudioAttributes> evaluateAudioAttributesToDuck(
List<AudioAttributes> activePlaybackAttributes) {
ArraySet<AudioAttributesWrapper> attributesToDuck = new ArraySet<>();
@@ -774,7 +788,6 @@
private final AudioAttributes mAudioAttributes;
- @VisibleForTesting
AudioAttributesWrapper(AudioAttributes audioAttributes) {
mAudioAttributes = audioAttributes;
}
diff --git a/service/src/com/android/car/audio/CarAudioContextInfo.java b/service/src/com/android/car/audio/CarAudioContextInfo.java
index 47b80af..f1b8410 100644
--- a/service/src/com/android/car/audio/CarAudioContextInfo.java
+++ b/service/src/com/android/car/audio/CarAudioContextInfo.java
@@ -19,6 +19,7 @@
import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
import android.media.AudioAttributes;
+import android.util.ArraySet;
import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
import com.android.car.internal.util.IndentingPrintWriter;
@@ -81,4 +82,45 @@
}
writer.decreaseIndent();
}
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CarAudioContextInfo)) {
+ return false;
+ }
+
+ CarAudioContextInfo info = (CarAudioContextInfo) other;
+
+ return mId == info.mId && mName.equals(info.mName)
+ && audioAttributesMatch(info.mAudioAttributes);
+ }
+
+ private boolean audioAttributesMatch(AudioAttributes[] audioAttributes) {
+ if (mAudioAttributes.length != audioAttributes.length) {
+ return false;
+ }
+
+ ArraySet<AudioAttributes> attributes =
+ new ArraySet<>(mAudioAttributes.length);
+ for (int index = 0; index < mAudioAttributes.length; index++) {
+ attributes.add(mAudioAttributes[index]);
+ }
+
+ for (int index = 0; index < audioAttributes.length; index++) {
+ if (!attributes.remove(audioAttributes[index])) {
+ return false;
+ }
+ }
+
+ return attributes.isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mName, Arrays.hashCode(mAudioAttributes));
+ }
}
diff --git a/service/src/com/android/car/audio/CarAudioFocus.java b/service/src/com/android/car/audio/CarAudioFocus.java
index cac3457..0787840 100644
--- a/service/src/com/android/car/audio/CarAudioFocus.java
+++ b/service/src/com/android/car/audio/CarAudioFocus.java
@@ -778,6 +778,10 @@
// Pull this entry out of the focus losers list
it.remove();
+ // Clear ducked status to prevent spurious LOSS_TRANSIENT to be sent while checking
+ // blocked entries and converting duckable loss to non-duckable
+ entry.setDucked(false);
+
// Add it back into the focus holders list
mFocusHolders.put(entry.getClientId(), entry);
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index 9552c4b..c499058 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -1303,12 +1303,16 @@
}
modifyAudioMirrorForZones(oldConfigs, newConfig);
- mCarAudioMirrorRequestHandler.updateRemoveMirrorConfigurationForZones(requestId, newConfig);
// If there are no more zones mirroring then turn it off at HAL
if (newConfig.length == 0) {
+ Slogf.i(TAG, "Sending mirror off command to audio HAL for address %s",
+ mirrorDevice.getAddress());
mAudioManager.setParameters(getAudioMirroringOffCommand(mirrorDevice.getAddress()));
}
+
+ //Send the signal to current listeners at the end
+ mCarAudioMirrorRequestHandler.updateRemoveMirrorConfigurationForZones(requestId, newConfig);
}
private void modifyAudioMirrorForZones(int[] audioZoneIds, int[] newConfig) {
@@ -2702,6 +2706,14 @@
userId, audioZoneId);
return;
}
+
+ // No need to undo focus or user device affinities.
+ // Focus is handled as user exits.
+ // User device affinities are handled below as the user id routing is undone.
+ removePrimaryZoneRequestForOccupantLocked(occupantZoneId, prevUserId);
+
+ removeAudioMirrorForZoneId(audioZoneId);
+
Slogf.d(TAG, "updateUserForOccupantZone assigning userId(%d) to audioZoneId(%d)",
userId, audioZoneId);
// If the user has changed, be sure to remove from current routing
@@ -2725,6 +2737,29 @@
setUserIdForAudioZoneLocked(userId, audioZoneId);
}
+ private void removeAudioMirrorForZoneId(int audioZoneId) {
+ long requestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(audioZoneId);
+ if (requestId == INVALID_REQUEST_ID) {
+ return;
+ }
+ Slogf.i(TAG, "Removing audio zone mirror for zone id %s", audioZoneId);
+ handleDisableAudioMirrorForZonesInConfig(new int[]{audioZoneId}, requestId);
+ }
+
+ @GuardedBy("mImplLock")
+ private void removePrimaryZoneRequestForOccupantLocked(int occupantZoneId, int userId) {
+ long requestId = mMediaRequestHandler.getAssignedRequestIdForOccupantZoneId(occupantZoneId);
+
+ if (requestId == INVALID_REQUEST_ID) {
+ return;
+ }
+
+ Slogf.d(TAG, "removePrimaryZoneRequestForOccupant removing request for %d occupant %d"
+ + " and user id %d", requestId, occupantZoneId, userId);
+ removeAssignedUserInfoLocked(userId);
+ mMediaRequestHandler.cancelMediaAudioOnPrimaryZone(requestId);
+ }
+
private int getOccupantZoneIdForDriver() {
List<CarOccupantZoneManager.OccupantZoneInfo> occupantZoneInfos =
mOccupantZoneService.getAllOccupantZones();
diff --git a/service/src/com/android/car/audio/CarAudioZonesHelper.java b/service/src/com/android/car/audio/CarAudioZonesHelper.java
index 355e02a..61d05b8 100644
--- a/service/src/com/android/car/audio/CarAudioZonesHelper.java
+++ b/service/src/com/android/car/audio/CarAudioZonesHelper.java
@@ -267,7 +267,7 @@
private void parseCarAudioContexts(XmlPullParser parser)
throws XmlPullParserException, IOException {
- int contextId = 0;
+ int contextId = CarAudioContext.getInvalidContext() + 1;
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
diff --git a/service/src/com/android/car/audio/CarVolumeGroup.java b/service/src/com/android/car/audio/CarVolumeGroup.java
index f093f92..b50fbbd 100644
--- a/service/src/com/android/car/audio/CarVolumeGroup.java
+++ b/service/src/com/android/car/audio/CarVolumeGroup.java
@@ -755,7 +755,9 @@
isAttenuated = isAttenuatedLocked() || isLimitedLocked();
}
- return new CarVolumeGroupInfo.Builder("group id " + mId, mZoneId, mId)
+ String name = mName.isEmpty() ? "group id " + mId : mName;
+
+ return new CarVolumeGroupInfo.Builder(name, mZoneId, mId)
.setVolumeGainIndex(gainIndex).setMaxVolumeGainIndex(getMaxGainIndex())
.setMinVolumeGainIndex(getMinGainIndex()).setMuted(isMuted).setBlocked(isBlocked)
.setAttenuated(isAttenuated).setAudioAttributes(getAudioAttributes()).build();
diff --git a/service/src/com/android/car/audio/FocusInteraction.java b/service/src/com/android/car/audio/FocusInteraction.java
index 276c1a8..9d5b822 100644
--- a/service/src/com/android/car/audio/FocusInteraction.java
+++ b/service/src/com/android/car/audio/FocusInteraction.java
@@ -353,10 +353,17 @@
mInteractionMatrix = new SparseArray<>(infos.size());
for (int rowIndex = 0; rowIndex < infos.size(); rowIndex++) {
CarAudioContextInfo rowInfo = infos.get(rowIndex);
+ int rowLegacyContext = CarAudioContext.getLegacyContextFromInfo(rowInfo);
SparseArray<Integer> rowDecisions = new SparseArray<>(infos.size());
for (int columnIndex = 0; columnIndex < infos.size(); columnIndex++) {
CarAudioContextInfo columnInfo = infos.get(columnIndex);
- rowDecisions.append(columnInfo.getId(), INTERACTION_CONCURRENT);
+ int columnLegacyContext = CarAudioContext.getLegacyContextFromInfo(columnInfo);
+ int focusDecision = CarAudioContext.isInvalidContextId(columnLegacyContext)
+ ? INTERACTION_REJECT
+ : CarAudioContext.isInvalidContextId(rowLegacyContext)
+ ? INTERACTION_EXCLUSIVE
+ : INTERACTION_MATRIX.get(rowLegacyContext).get(columnLegacyContext);
+ rowDecisions.append(columnInfo.getId(), focusDecision);
}
mInteractionMatrix.append(rowInfo.getId(), rowDecisions);
}
diff --git a/service/src/com/android/car/audio/MediaRequestHandler.java b/service/src/com/android/car/audio/MediaRequestHandler.java
index ab46449..949c61f 100644
--- a/service/src/com/android/car/audio/MediaRequestHandler.java
+++ b/service/src/com/android/car/audio/MediaRequestHandler.java
@@ -224,6 +224,23 @@
}
}
+ long getAssignedRequestIdForOccupantZoneId(int occupantZoneId) {
+ CarOccupantZoneManager.OccupantZoneInfo occupantZoneInfo = null;
+ synchronized (mLock) {
+ for (int index = 0; index < mAssignedOccupants.size(); index++) {
+ CarOccupantZoneManager.OccupantZoneInfo info = mAssignedOccupants.valueAt(index);
+ if (info.zoneId != occupantZoneId) {
+ continue;
+ }
+ occupantZoneInfo = info;
+ break;
+ }
+ }
+
+ return occupantZoneInfo == null
+ ? INVALID_REQUEST_ID : getRequestIdForOccupant(occupantZoneInfo);
+ }
+
List<Long> getRequestsOwnedByApprover(IPrimaryZoneMediaAudioRequestCallback callback) {
List<Long> ownedRequests = new ArrayList<>();
synchronized (mLock) {
@@ -397,8 +414,8 @@
InternalMediaAudioRequest value = mMediaAudioRequestIdToCallback.valueAt(index);
writer.printf("%d : %s\n", key, value);
}
- writer.increaseIndent();
- writer.printf("Request id to approver[%d]\n", mRequestIdToApprover.size());
+ writer.decreaseIndent();
+ writer.printf("Request id to approver[%d]:\n", mRequestIdToApprover.size());
writer.increaseIndent();
for (int index = 0; index < mRequestIdToApprover.size(); index++) {
long key = mRequestIdToApprover.keyAt(index);
diff --git a/service/src/com/android/car/bluetooth/BluetoothDeviceManager.java b/service/src/com/android/car/bluetooth/BluetoothDeviceManager.java
index daaadae..747a371 100644
--- a/service/src/com/android/car/bluetooth/BluetoothDeviceManager.java
+++ b/service/src/com/android/car/bluetooth/BluetoothDeviceManager.java
@@ -186,7 +186,14 @@
// If a device was allowed to connected then its assumed it has a connection policy
// value of ALLOWED.
addDevice(device);
- triggerConnections(device);
+
+ // Do not trigger connections based on the connection status of PAN. PAN is known to
+ // allow connections, just to tear it down immediately. A race condition can cause the
+ // connection to continually be brought up and down as a result, unless timing is
+ // perfect
+ if (profile != BluetoothProfile.PAN) {
+ triggerConnections(device);
+ }
}
}
diff --git a/service/src/com/android/car/evs/CarEvsService.java b/service/src/com/android/car/evs/CarEvsService.java
index ad2605e..a35f162 100644
--- a/service/src/com/android/car/evs/CarEvsService.java
+++ b/service/src/com/android/car/evs/CarEvsService.java
@@ -26,6 +26,9 @@
import static android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED;
import static com.android.car.CarLog.TAG_EVS;
+import static com.android.car.evs.StateMachine.REQUEST_PRIORITY_LOW;
+import static com.android.car.evs.StateMachine.REQUEST_PRIORITY_NORMAL;
+import static com.android.car.evs.StateMachine.REQUEST_PRIORITY_HIGH;
import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
import android.annotation.NonNull;
@@ -40,7 +43,6 @@
import android.car.evs.CarEvsManager.CarEvsError;
import android.car.evs.CarEvsManager.CarEvsServiceState;
import android.car.evs.CarEvsManager.CarEvsServiceType;
-import android.car.evs.CarEvsManager.CarEvsStreamEvent;
import android.car.evs.CarEvsStatus;
import android.car.evs.ICarEvsStatusListener;
import android.car.evs.ICarEvsStreamCallback;
@@ -49,11 +51,10 @@
import android.car.hardware.property.ICarPropertyEventListener;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.hardware.HardwareBuffer;
import android.hardware.automotive.vehicle.VehicleGear;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -64,25 +65,28 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import com.android.car.BuiltinPackageDependency;
import com.android.car.CarPropertyService;
import com.android.car.CarServiceBase;
import com.android.car.CarServiceUtils;
import com.android.car.R;
import com.android.car.hal.EvsHalService;
import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+import com.android.car.internal.evs.CarEvsUtils;
import com.android.car.internal.evs.EvsHalWrapper;
import com.android.car.internal.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* A service that listens to the Extended View System across a HAL boundary and exposes the data to
@@ -108,23 +112,11 @@
* See CarEvsService.StateMachine class for more details.
*/
public final class CarEvsService extends android.car.evs.ICarEvsService.Stub
- implements CarServiceBase, EvsHalService.EvsHalEventListener,
- EvsHalWrapper.HalEventCallback {
+ implements CarServiceBase {
private static final boolean DBG = Slogf.isLoggable(TAG_EVS, Log.DEBUG);
- // Timeout for a request to start a video stream with a valid token
- private static final int STREAM_START_REQUEST_TIMEOUT_MS = 3000;
-
- // Interval for connecting to the EVS HAL service trial
- private static final long EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS = 1000;
-
- // Service request priorities
- private static final int REQUEST_PRIORITY_LOW = 0;
- private static final int REQUEST_PRIORITY_NORMAL = 1;
- private static final int REQUEST_PRIORITY_HIGH = 2;
-
- private static final class EvsHalEvent {
+ static final class EvsHalEvent {
private long mTimestamp;
private int mServiceType;
private boolean mOn;
@@ -149,30 +141,23 @@
@ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
public String toString() {
- return "ServiceType = " + mServiceType + ", mOn = " + mOn +
- ", Timestamp = " + mTimestamp;
+ return "ServiceType=" + CarEvsUtils.convertToString(mServiceType) +
+ ", mOn=" + mOn + ", Timestamp=" + mTimestamp;
}
}
- private static final String COMMAND_TO_USE_DEFAULT_CAMERA = "default";
-
- private final EvsHalWrapper mHalWrapper;
-
private final Context mContext;
+ private final Context mBuiltinContext;
private final EvsHalService mEvsHalService;
private final CarPropertyService mPropertyService;
private final DisplayManager mDisplayManager; // To monitor the default display's state
private final Object mLock = new Object();
-
- private final ComponentName mEvsCameraActivity;
+ private final ArraySet<IBinder> mSessionTokens = new ArraySet<>();
// This handler is to monitor the client sends a video stream request within a given time
// after a state transition to the REQUESTED state.
private final Handler mHandler = new Handler(Looper.getMainLooper());
- // Bookkeeps received frame buffers
- private final ArraySet mBufferRecords = new ArraySet();
-
private final class StatusListenerList extends RemoteCallbackList<ICarEvsStatusListener> {
private final WeakReference<CarEvsService> mService;
@@ -194,22 +179,6 @@
private final StatusListenerList mStatusListeners = new StatusListenerList(this);
- private final IBinder.DeathRecipient mStreamCallbackDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Slogf.w(TAG_EVS, "StreamCallback has died");
- synchronized (mLock) {
- if (requestActivityIfNecessaryLocked()) {
- Slogf.i(TAG_EVS, "Requested to launch the activity.");
- } else {
- // Ensure we stops streaming
- handleClientDisconnected(mStreamCallback);
- }
- }
- }
- };
-
/**
* {@link CarPropertyEvent} listener registered with {@link CarPropertyService} to listen to
* {@link VehicleProperty.GEAR_SELECTION} change notifications.
@@ -221,455 +190,93 @@
if (events.isEmpty()) {
return;
}
- synchronized (mLock) {
- // Handle only the latest event
- Slogf.i(TAG_EVS, "Handling GearSelection event");
- handlePropertyEventLocked(events.get(events.size() - 1));
- }
+
+ // Handle only the latest event
+ Slogf.i(TAG_EVS, "Handling GearSelection event");
+ handlePropertyEvent(events.get(events.size() - 1));
}
};
- private final Runnable mActivityRequestTimeoutRunnable = () -> handleActivityRequestTimeout();
-
- private final DisplayManager.DisplayListener mDisplayListener =
- new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- // Nothing to do
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- // Nothing to do
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // We are interested only in the default display.
- return;
- }
-
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- switch (display.getState()) {
- case Display.STATE_ON:
- // We may want to request the system viewer.
- synchronized (mLock) {
- if (!requestActivityIfNecessaryLocked()) {
- Slogf.e(TAG_EVS, "Failed to request the system viewer");
- }
- }
- break;
-
- case Display.STATE_OFF:
- // Stop an active client
- ICarEvsStreamCallback callback;
- synchronized (mLock) {
- callback = mStreamCallback;
- }
- if (callback != null) {
- stopVideoStream(callback);
- }
- break;
-
- default:
- // Nothing to do for all other state changes
- break;
- }
- }
- };
-
- // CarEvsService state machine implementation to handle all state transitions.
- private final class StateMachine {
- // Current state
- @GuardedBy("mLock")
- private int mState = SERVICE_STATE_UNAVAILABLE;
-
- // Current service type
- @GuardedBy("mLock")
- private int mServiceType = CarEvsManager.SERVICE_TYPE_REARVIEW;
-
- // Priority of a last service request
- @GuardedBy("mLock")
- private int mLastRequestPriority = REQUEST_PRIORITY_LOW;
-
- public @CarEvsError int execute(int priority, int destination) {
- int serviceType;
- synchronized (mLock) {
- serviceType = mServiceType;
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ // Nothing to do
}
- return execute(priority, destination, serviceType, null, null);
- }
- public @CarEvsError int execute(int priority, int destination, int service) {
- return execute(priority, destination, service, null, null);
- }
-
- public @CarEvsError int execute(int priority, int destination,
- ICarEvsStreamCallback callback) {
- int serviceType;
- synchronized (mLock) {
- serviceType = mServiceType;
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // Nothing to do
}
- return execute(priority, destination, serviceType, null, callback);
- }
- public @CarEvsError int execute(int priority, int destination, int service, IBinder token,
- ICarEvsStreamCallback callback) {
-
- int serviceType;
- int newState;
- int result = ERROR_NONE;
- synchronized (mLock) {
- // TODO(b/188970686): Reduce this lock duration.
- if (mState == destination && priority < mLastRequestPriority &&
- destination != SERVICE_STATE_REQUESTED) {
- // Nothing to do
- return ERROR_NONE;
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ // We are interested only in the default display.
+ return;
}
- int previousState = mState;
- Slogf.i(TAG_EVS, "Transition requested: %s -> %s", stateToString(previousState),
- stateToString(destination));
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ if (mCurrentDisplayState == display.getState()) {
+ // We already handled this display state change.
+ Slogf.i(TAG_EVS, "We already handled a reported display status, %d",
+ display.getState());
+ return;
+ }
- switch (destination) {
- case SERVICE_STATE_UNAVAILABLE:
- result = handleTransitionToUnavailableLocked();
+ // TODO(b/292155786): Current implementation is optimized for the device with a
+ // single display and therefore we may need to consider the
+ // source of the display event and start/stop activities
+ // accordingly.
+ switch (display.getState()) {
+ case Display.STATE_ON:
+ // Requests each StateMachine to launch a registered activity if it's
+ // necessary.
+ for (int i = 0; i < mServiceInstances.size(); i++) {
+ if (mServiceInstances.valueAt(i)
+ .requestStartActivityIfNecessary() == ERROR_NONE) {
+ continue;
+ }
+ Slogf.e(TAG_EVS, "Failed to start %s's activity.",
+ CarEvsUtils.convertToString(mServiceInstances.keyAt(i)));
+ }
break;
- case SERVICE_STATE_INACTIVE:
- result = handleTransitionToInactiveLocked(priority, service, callback);
- break;
-
- case SERVICE_STATE_REQUESTED:
- result = handleTransitionToRequestedLocked(priority, service);
- break;
-
- case SERVICE_STATE_ACTIVE:
- result = handleTransitionToActiveLocked(priority, service, token, callback);
+ case Display.STATE_OFF:
+ // Each StateMachine stores a valid session token that was used for
+ // recognizing a streaming callback from a launched activity.
+ // CarEvsService will request each StateMachine to stop those callbacks
+ // and let other callbacks continue running. Activities not launched by
+ // CarEvsService must handle display's state changes properly by
+ // themselves.
+ for (int i = 0; i < mServiceInstances.size(); i++) {
+ mServiceInstances.valueAt(i)
+ .requestStopActivity(REQUEST_PRIORITY_HIGH);
+ }
break;
default:
- throw new IllegalStateException(
- "CarEvsService is in the unknown state, " + previousState);
- }
-
- serviceType = mServiceType;
- newState = mState;
- }
-
- if (result == ERROR_NONE) {
- Slogf.i(TAG_EVS, "Transition completed: %s", stateToString(destination));
- // Broadcasts current state
- broadcastStateTransition(serviceType, newState);
- } else {
- Slogf.e(TAG_EVS, "Transition failed: error = %d", result);
- }
-
- return result;
- }
-
- public @CarEvsServiceState int getState() {
- synchronized (mLock) {
- return mState;
- }
- }
-
- @VisibleForTesting
- void setState(@CarEvsServiceState int newState) {
- synchronized (mLock) {
- mState = newState;
- }
- }
-
- public @CarEvsServiceType int getServiceType() {
- synchronized (mLock) {
- return mServiceType;
- }
- }
-
- public CarEvsStatus getStateAndServiceType() {
- synchronized (mLock) {
- return new CarEvsStatus(getServiceType(), getState());
- }
- }
-
- public boolean checkCurrentStateRequiresSystemActivity() {
- synchronized (mLock) {
- return (mState == SERVICE_STATE_ACTIVE || mState == SERVICE_STATE_REQUESTED) &&
- mLastRequestPriority == REQUEST_PRIORITY_HIGH;
- }
- }
-
- @GuardedBy("mLock")
- private @CarEvsError int handleTransitionToUnavailableLocked() {
- // This transition happens only when CarEvsService loses the active connection to the
- // Extended View System service.
- switch (mState) {
- case SERVICE_STATE_UNAVAILABLE:
- // Nothing to do
- break;
-
- default:
- // Stops any active video stream
- stopService();
- break;
- }
-
- mState = SERVICE_STATE_UNAVAILABLE;
- return ERROR_NONE;
- }
-
- @GuardedBy("mLock")
- private @CarEvsError int handleTransitionToInactiveLocked(int priority, int service,
- ICarEvsStreamCallback callback) {
-
- switch (mState) {
- case SERVICE_STATE_UNAVAILABLE:
- if (callback != null) {
- // We get a request to stop a video stream after losing a native EVS
- // service. Simply unregister a callback and return.
- unlinkToDeathStreamCallbackLocked();
- mStreamCallback = null;
- return ERROR_NONE;
- } else {
- // Requested to connect to the Extended View System service
- if (!mHalWrapper.connectToHalServiceIfNecessary()) {
- return ERROR_UNAVAILABLE;
- }
-
- if (mStateEngine.checkCurrentStateRequiresSystemActivity() ||
- (mLastEvsHalEvent != null &&
- mLastEvsHalEvent.isRequestingToStartActivity())) {
- // Request to launch the viewer because we lost the Extended View System
- // service while a client was actively streaming a video.
- mHandler.postDelayed(mActivityRequestTimeoutRunnable,
- STREAM_START_REQUEST_TIMEOUT_MS);
- }
- }
- break;
-
- case SERVICE_STATE_INACTIVE:
- // Nothing to do
- break;
-
- case SERVICE_STATE_REQUESTED:
- // Requested to cancel a pending service request
- if (mServiceType != service || priority < mLastRequestPriority) {
- return ERROR_BUSY;
- }
-
- // Reset a timer for this new request
- mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
- break;
-
- case SERVICE_STATE_ACTIVE:
- // Requested to stop a current video stream
- if (mServiceType != service || priority < mLastRequestPriority) {
- return ERROR_BUSY;
- }
-
- stopService(callback);
- break;
-
- default:
- throw new IllegalStateException("CarEvsService is in the unknown state.");
- }
-
- mState = SERVICE_STATE_INACTIVE;
- setSessionToken(null);
- return ERROR_NONE;
- }
-
- @GuardedBy("mLock")
- private @CarEvsError int handleTransitionToRequestedLocked(int priority, int service) {
- switch (mState) {
- case SERVICE_STATE_UNAVAILABLE:
- // Attempts to connect to the native EVS service and transits to the
- // REQUESTED state if it succeeds.
- if (!mHalWrapper.connectToHalServiceIfNecessary()) {
- return ERROR_UNAVAILABLE;
- }
- break;
-
- case SERVICE_STATE_INACTIVE:
- // Nothing to do
- break;
-
- case SERVICE_STATE_REQUESTED:
- if (priority < mLastRequestPriority) {
- // A current service request has a lower priority than a previous
- // service request.
- Slogf.e(TAG_EVS, "CarEvsService is busy with a higher priority client.");
- return ERROR_BUSY;
- }
-
- // Reset a timer for this new request
- mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
- break;
-
- case SERVICE_STATE_ACTIVE:
- if (priority < mLastRequestPriority) {
- // We decline a request because CarEvsService is busy with a higher priority
- // client.
- return ERROR_BUSY;
- } else if (priority == mLastRequestPriority) {
- // We do not need to transit to the REQUESTED state because CarEvsService
- // was transited to the ACTIVE state by a request that has the same priority
- // with current request.
- return ERROR_NONE;
- } else {
- // Stop stream on all lower priority clients.
- processStreamEvent(STREAM_EVENT_STREAM_STOPPED);
- }
- break;
-
- default:
- throw new IllegalStateException("CarEvsService is in the unknown state.");
- }
-
- // Arms the timer for the high-priority request
- if (priority == REQUEST_PRIORITY_HIGH) {
- mHandler.postDelayed(
- mActivityRequestTimeoutRunnable, STREAM_START_REQUEST_TIMEOUT_MS);
- }
-
- mState = SERVICE_STATE_REQUESTED;
- mServiceType = service;
- mLastRequestPriority = priority;
-
- if (mEvsCameraActivity != null) {
- Intent evsIntent = new Intent(Intent.ACTION_MAIN)
- .setComponent(mEvsCameraActivity)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
- .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- if (priority == REQUEST_PRIORITY_HIGH) {
- mSessionToken = new Binder();
- Bundle bundle = new Bundle();
- bundle.putBinder(CarEvsManager.EXTRA_SESSION_TOKEN, mSessionToken);
- evsIntent.replaceExtras(bundle);
- }
- mContext.startActivity(evsIntent);
- }
- return ERROR_NONE;
- }
-
- @GuardedBy("mLock")
- private @CarEvsError int handleTransitionToActiveLocked(int priority, int service,
- IBinder token, ICarEvsStreamCallback callback) {
-
- @CarEvsError int result = ERROR_NONE;
- switch (mState) {
- case SERVICE_STATE_UNAVAILABLE:
- // We do not have a valid connection to the Extended View System service.
- return ERROR_UNAVAILABLE;
-
- case SERVICE_STATE_INACTIVE:
- // CarEvsService receives a low priority request to start a video stream.
- result = startServiceAndVideoStream(service, callback);
- if (result != ERROR_NONE) {
- return result;
- }
- break;
-
- case SERVICE_STATE_REQUESTED:
- // CarEvsService is reserved for higher priority clients.
- if (priority == REQUEST_PRIORITY_HIGH && !isSessionToken(token)) {
- // Declines a request with an expired token.
- return ERROR_BUSY;
- }
-
- result = startServiceAndVideoStream(service, callback);
- if (result != ERROR_NONE) {
- return result;
- }
- break;
-
- case SERVICE_STATE_ACTIVE:
- // CarEvsManager will transfer an active video stream to a new client with a
- // higher or equal priority.
- if (priority < mLastRequestPriority) {
- Slogf.i(TAG_EVS, "Declines a service request with a lower priority.");
+ // Nothing to do for all other state changes
break;
- }
+ }
- if (mStreamCallback != null) {
- // keep old reference for Runnable.
- ICarEvsStreamCallback previousCallback = mStreamCallback;
- mStreamCallback = null;
- mHandler.post(() -> notifyStreamStopped(previousCallback));
- }
-
- mStreamCallback = callback;
- break;
-
- default:
- throw new IllegalStateException("CarEvsService is in the unknown state.");
+ mCurrentDisplayState = display.getState();
}
+ };
- mState = SERVICE_STATE_ACTIVE;
- mServiceType = service;
- mLastRequestPriority = priority;
- return ERROR_NONE;
- }
+ // Service instances per each type.
+ private final SparseArray<StateMachine> mServiceInstances;
- @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
- private String stateToString(@CarEvsServiceState int state) {
- switch (state) {
- case SERVICE_STATE_UNAVAILABLE:
- return "UNAVAILABLE";
- case SERVICE_STATE_INACTIVE:
- return "INACTIVE";
- case SERVICE_STATE_REQUESTED:
- return "REQUESTED";
- case SERVICE_STATE_ACTIVE:
- return "ACTIVE";
- default:
- return "UNKNOWN";
- }
- }
+ // Associates callback objects with their service types.
+ private final ArrayMap<IBinder, ArraySet<Integer>> mCallbackToServiceType =
+ new ArrayMap<>();
- @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
- public String toString() {
- synchronized (mLock) {
- return stateToString(mState);
- }
- }
- }
-
- private final StateMachine mStateEngine = new StateMachine();
-
- @GuardedBy("mLock")
- private ICarEvsStreamCallback mStreamCallback = null;
-
- // The latest session token issued to the privileged clients
- @GuardedBy("mLock")
- private IBinder mSessionToken = null;
+ // The latest display state we have processed.
+ private int mCurrentDisplayState = Display.STATE_OFF;
// This boolean flag is true if CarEvsService uses GEAR_SELECTION VHAL property instead of
// EVS_SERVICE_REQUEST.
private boolean mUseGearSelection = true;
- // When this is set, CarEvsService will attempt to open a camera device the user sets.
- private boolean mUseCameraIdOverride = false;
-
- // This is a device name to be used when mUseCameraIdOverride is true.
- private String mCameraIdOverride;
-
- private void setSessionToken(IBinder token) {
- synchronized (mLock) {
- mSessionToken = token;
- }
- }
-
- private boolean isSessionToken(IBinder token) {
- synchronized (mLock) {
- return token != null && token == mSessionToken;
- }
- }
-
// The last event EvsHalService reported. This will be set to null when a related service
// request is handled.
//
@@ -679,162 +286,118 @@
private EvsHalEvent mLastEvsHalEvent = new EvsHalEvent(/* timestamp= */ 0,
CarEvsManager.SERVICE_TYPE_REARVIEW, /* on= */ false);
- // Starts a service and its video stream
- @GuardedBy("mLock")
- private @CarEvsError int startServiceAndVideoStream(
- @CarEvsServiceType int service, ICarEvsStreamCallback callback) {
- if (!startService(service)) {
- return ERROR_UNAVAILABLE;
- }
-
- mStreamCallback = callback;
- linkToDeathStreamCallbackLocked();
-
- if (!mHalWrapper.requestToStartVideoStream()) {
- Slogf.e(TAG_EVS, "Failed to start a video stream");
- mStreamCallback = null;
- return ERROR_UNAVAILABLE;
- }
-
- return ERROR_NONE;
- }
-
- @GuardedBy("mLock")
- private boolean requestActivityIfNecessaryLocked() {
- // TODO(b/202398413): add a test case to verify below logic
- if (!mStateEngine.checkCurrentStateRequiresSystemActivity() &&
- (mLastEvsHalEvent == null || !mLastEvsHalEvent.isRequestingToStartActivity())) {
- return false;
- }
-
- // Request to launch an activity again after cleaning up
- mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE);
- mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_REQUESTED,
- mLastEvsHalEvent.getServiceType());
- return true;
- }
-
- // Waits for a video stream request from the System UI with a valid token.
- private void handleActivityRequestTimeout() {
- synchronized (mLock) {
- // No client has responded to a state transition to the REQUESTED
- // state before the timer expires. CarEvsService sends a
- // notification again if it's still needed.
- if (requestActivityIfNecessaryLocked()) {
- Slogf.w(TAG_EVS, "Timer expired. Request to launch the activity again.");
- return;
- } else if (mStateEngine.getState() == SERVICE_STATE_REQUESTED) {
- // If the service is no longer required by other services, we transit to
- // the INACTIVE state.
- mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE);
- }
- }
- }
-
- @GuardedBy("mLock")
- private void linkToDeathStreamCallbackLocked() {
- IBinder binder;
- if (mStreamCallback == null) {
- return;
- }
-
- binder = mStreamCallback.asBinder();
- if (binder == null) {
- Slogf.w(TAG_EVS, "Linking to a binder death recipient skipped");
- return;
- }
-
- try {
- binder.linkToDeath(mStreamCallbackDeathRecipient, 0);
- } catch (RemoteException e) {
- Slogf.w(TAG_EVS, "Failed to link a binder death recipient: " + e);
- }
- }
-
- @GuardedBy("mLock")
- private void unlinkToDeathStreamCallbackLocked() {
- IBinder binder;
- if (mStreamCallback == null) {
- return;
- }
-
- binder = mStreamCallback.asBinder();
- if (binder == null) {
- return;
- }
-
- binder.unlinkToDeath(mStreamCallbackDeathRecipient, 0);
- }
-
/** Creates an Extended View System service instance given a {@link Context}. */
public CarEvsService(Context context, Context builtinContext, EvsHalService halService,
CarPropertyService propertyService) {
mContext = context;
+ mBuiltinContext = builtinContext;
mPropertyService = propertyService;
mEvsHalService = halService;
- mHalWrapper = createHalWrapper(builtinContext, this);
+ // Reads the service configuration and initializes service instances.
+ String[] rawConfigurationStrings = mContext.getResources()
+ .getStringArray(R.array.config_carEvsService);
+ if (rawConfigurationStrings != null && rawConfigurationStrings.length > 0) {
+ mServiceInstances = new SparseArray<>(rawConfigurationStrings.length);
+ for (String rawString : rawConfigurationStrings) {
+ CarEvsServiceUtils.Parameters params = CarEvsServiceUtils.parse(rawString);
- String activityName = mContext.getResources().getString(R.string.config_evsCameraActivity);
- if (!activityName.isEmpty()) {
- mEvsCameraActivity = ComponentName.unflattenFromString(activityName);
+ StateMachine s = new StateMachine(context, builtinContext, this,
+ params.getActivityComponentName(), params.getType(), params.getCameraId());
+ mServiceInstances.put(params.getType(), s);
+ }
+
+ if (mServiceInstances.size() < 1) {
+ Slogf.e(TAG_EVS, "No valid configuration has been found. " +
+ "CarEvsService won't be available.");
+ mDisplayManager = null;
+ return;
+ }
} else {
- mEvsCameraActivity = null;
+ mServiceInstances = new SparseArray<>(/* capacity= */ 1);
+ Slogf.i(TAG_EVS, "CarEvsService will be initialized only for the rearview service " +
+ "because no service configuration was available via " +
+ "config_carEvsService.");
+
+ String activityName = mContext.getResources()
+ .getString(R.string.config_evsCameraActivity);
+ ComponentName activityComponentName;
+ if (!activityName.isEmpty()) {
+ activityComponentName = ComponentName.unflattenFromString(activityName);
+ } else {
+ activityComponentName = null;
+ }
+ if (DBG) Slogf.d(TAG_EVS, "evsCameraActivity=" + activityName);
+
+ String cameraId = context.getString(R.string.config_evsRearviewCameraId);
+ StateMachine s = new StateMachine(context, builtinContext, this, activityComponentName,
+ CarEvsManager.SERVICE_TYPE_REARVIEW, cameraId);
+ mServiceInstances.put(CarEvsManager.SERVICE_TYPE_REARVIEW, s);
}
- if (DBG) Slogf.d(TAG_EVS, "evsCameraActivity=" + mEvsCameraActivity);
mDisplayManager = context.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
}
- static EvsHalWrapper createHalWrapper(Context builtinContext,
- EvsHalWrapper.HalEventCallback callback) {
- try {
- Class helperClass = builtinContext.getClassLoader().loadClass(
- BuiltinPackageDependency.EVS_HAL_WRAPPER_CLASS);
- Constructor constructor = helperClass.getConstructor(
- new Class[]{EvsHalWrapper.HalEventCallback.class});
- return (EvsHalWrapper) constructor.newInstance(callback);
- } catch (Exception e) {
- throw new RuntimeException(
- "Cannot load class:" + BuiltinPackageDependency.EVS_HAL_WRAPPER_CLASS, e);
- }
- }
+ @VisibleForTesting
+ final class EvsTriggerListener implements EvsHalService.EvsHalEventListener {
- /** Implements EvsHalService.EvsHalEventListener to monitor VHAL properties. */
- @Override
- public void onEvent(@CarEvsServiceType int type, boolean on) {
- if (DBG) {
- Slogf.d(TAG_EVS,
- "Received an event from EVS HAL: type = " + type + ", on = " + on);
- }
-
- synchronized (mLock) {
- int targetState = on ? SERVICE_STATE_REQUESTED : SERVICE_STATE_INACTIVE;
- if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, targetState, type, /* token = */ null,
- mStreamCallback) != ERROR_NONE) {
- Slogf.e(TAG_EVS, "Failed to execute a service request.");
+ /** Implements EvsHalService.EvsHalEventListener to monitor VHAL properties. */
+ @Override
+ public void onEvent(@CarEvsServiceType int type, boolean on) {
+ if (DBG) {
+ Slogf.d(TAG_EVS,
+ "Received an event from EVS HAL: type = " + type + ", on = " + on);
}
- // Stores the last event
- mLastEvsHalEvent = new EvsHalEvent(SystemClock.elapsedRealtimeNanos(), type, on);
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ Slogf.w(TAG_EVS, "CarEvsService is not configured for %s", type);
+ return;
+ }
+
+ // Stores the last event.
+ synchronized (mLock) {
+ mLastEvsHalEvent = new EvsHalEvent(SystemClock.elapsedRealtimeNanos(), type, on);
+ }
+
+ if (on) {
+ // Request a camera activity.
+ if (instance.requestStartActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) {
+ Slogf.e(TAG_EVS, "Fail to request a registered activity.");
+ }
+ } else {
+ // Stop a video stream and close an activity.
+ if (instance.requestStopActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) {
+ Slogf.e(TAG_EVS, "Fail to stop a registered activity.");
+ }
+ }
}
}
+ @VisibleForTesting
+ final EvsTriggerListener mEvsTriggerListener = new EvsTriggerListener();
+
@Override
public void init() {
if (DBG) {
Slogf.d(TAG_EVS, "Initializing the service");
}
- if (!mHalWrapper.init()) {
- Slogf.e(TAG_EVS, "Failed to initialize a service handle");
- return;
+ for (int i = mServiceInstances.size() - 1; i >= 0; i--) {
+ StateMachine instance = mServiceInstances.valueAt(i);
+ if (instance.init()) {
+ continue;
+ }
+
+ Slogf.e(TAG_EVS, "Failed to initialize a service handle for %s.",
+ mServiceInstances.keyAt(i));
+ mServiceInstances.removeAt(i);
}
if (mEvsHalService.isEvsServiceRequestSupported()) {
try {
- mEvsHalService.setListener(this);
+ mEvsHalService.setListener(mEvsTriggerListener);
if (DBG) {
Slogf.d(TAG_EVS, "CarEvsService listens to EVS_SERVICE_REQUEST property.");
}
@@ -846,25 +409,33 @@
}
if (mUseGearSelection) {
- if (DBG) {
- Slogf.d(TAG_EVS, "CarEvsService listens to GEAR_SELECTION property.");
- }
-
if (mPropertyService == null || mPropertyService.getPropertySafe(
VehiclePropertyIds.GEAR_SELECTION, /*areaId=*/ 0) == null) {
- Slogf.e(TAG_EVS,
- "CarEvsService is disabled because GEAR_SELECTION is unavailable.");
+ Slogf.w(TAG_EVS,
+ "GEAR_SELECTION property is also not available. " +
+ "CarEvsService may not respond to the system events.");
mUseGearSelection = false;
- return;
- }
+ } else {
+ if (DBG) {
+ Slogf.d(TAG_EVS, "CarEvsService listens to GEAR_SELECTION property.");
+ }
- mPropertyService.registerListenerSafe(
- VehiclePropertyIds.GEAR_SELECTION, /*updateRateHz=*/0,
- mGearSelectionPropertyListener);
+ if (!mPropertyService.registerListenerSafe(
+ VehiclePropertyIds.GEAR_SELECTION, /*updateRateHz=*/0,
+ mGearSelectionPropertyListener)) {
+ Slogf.w(TAG_EVS, "Failed to register a listener for GEAR_SELECTION property.");
+ mUseGearSelection = false;
+ }
+ }
}
- // Attempts to transit to the INACTIVE state
- connectToHalServiceIfNecessary(EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS);
+ StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ if (instance == null) {
+ Slogf.w(TAG_EVS, "The service is not initialized for the rearview service.");
+ return;
+ }
+
+ instance.connectToHalServiceIfNecessary();
}
@Override
@@ -881,26 +452,30 @@
mGearSelectionPropertyListener);
}
- mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
+ for (int i = 0; i < mServiceInstances.size(); i++) {
+ StateMachine instance = mServiceInstances.valueAt(i);
+ instance.release();
+ }
+
mStatusListeners.kill();
- mHalWrapper.release();
}
@Override
@ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
public void dump(IndentingPrintWriter writer) {
writer.println("*CarEvsService*");
- writer.printf("Current state = %s\n", mStateEngine);
- writer.printf("%s to HAL service\n",
- mHalWrapper.isConnected() ? "Connected" : "Not connected");
+
+ writer.increaseIndent();
+ for (int i = 0; i < mServiceInstances.size(); i++) {
+ mServiceInstances.valueAt(i).dump(writer);
+ }
+ writer.decreaseIndent();
+ writer.printf("\n");
synchronized (mLock) {
- writer.printf("Active stream client = %s\n",
- mStreamCallback == null ? "null" : mStreamCallback.asBinder());
writer.printf("%d service listeners subscribed.\n",
mStatusListeners.getRegisteredCallbackCount());
- writer.printf("Last HAL event = %s\n", mLastEvsHalEvent);
- writer.printf("Current session token = %s\n", mSessionToken);
+ writer.printf("Last HAL event: %s\n", mLastEvsHalEvent);
}
}
@@ -953,7 +528,18 @@
public @CarEvsError int startActivity(int type) {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY);
- return mStateEngine.execute(REQUEST_PRIORITY_NORMAL, SERVICE_STATE_REQUESTED, type);
+ if (type == CarEvsManager.SERVICE_TYPE_SURROUNDVIEW) {
+ // TODO(b/179029031): Removes below when Surround View service is integrated.
+ Slogf.e(TAG_EVS, "Surround view is not supported yet.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ return ERROR_UNAVAILABLE;
+ }
+
+ return instance.requestStartActivity(REQUEST_PRIORITY_NORMAL);
}
/**
@@ -966,7 +552,12 @@
public void stopActivity() {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY);
- mStateEngine.execute(REQUEST_PRIORITY_NORMAL, SERVICE_STATE_INACTIVE, mStreamCallback);
+ StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ if (instance == null) {
+ return;
+ }
+
+ instance.requestStopActivity(REQUEST_PRIORITY_NORMAL);
}
/**
@@ -986,19 +577,28 @@
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
Objects.requireNonNull(callback);
- int priority;
- if (isSessionToken(token)) {
- mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
- priority = REQUEST_PRIORITY_HIGH;
- } else {
- priority = REQUEST_PRIORITY_LOW;
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ Slogf.e(TAG_EVS, "CarEvsService is not configured for a service type %d.", type);
+ return ERROR_UNAVAILABLE;
}
- return mStateEngine.execute(priority, SERVICE_STATE_ACTIVE, type, token, callback);
+ // Single client can subscribe to multiple services.
+ // ArrayMap<IBinder, ArraySet<Integer>>
+ // Remembers which service a given callback is subscribing to.
+ ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder());
+ if (types == null) {
+ mCallbackToServiceType.put(callback.asBinder(),
+ new ArraySet<>(Set.of(new Integer(type))));
+ } else {
+ types.add(type);
+ }
+
+ return instance.requestStartVideoStream(callback, token);
}
/**
- * Requests to stop a video stream from the current service.
+ * Requests to stop a video stream from the current client.
*
* <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
*
@@ -1008,20 +608,25 @@
public void stopVideoStream(@NonNull ICarEvsStreamCallback callback) {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
Objects.requireNonNull(callback);
- synchronized (mLock) {
- if (mStreamCallback == null || callback.asBinder() != mStreamCallback.asBinder()) {
- Slogf.i(TAG_EVS, "Ignores a video stream request not from current stream client.");
- return;
- }
- }
- if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback) !=
- ERROR_NONE) {
- Slogf.w(TAG_EVS, "Failed to stop a video stream");
-
- // We want to return if a video stop request fails.
+ ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder());
+ if (types == null || types.isEmpty()) {
+ Slogf.i(TAG_EVS, "Ignores a request to stop a video stream for unknown callback %s.",
+ callback);
return;
}
+
+ for (int i = 0; i < types.size(); i++) {
+ int type = types.valueAt(i);
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ Slogf.w(TAG_EVS, "CarEvsService is not configured for a service type %d.", type);
+ continue;
+ }
+
+ instance.requestStopVideoStream(callback);
+ }
+ mCallbackToServiceType.remove(callback.asBinder());
}
/**
@@ -1038,22 +643,13 @@
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
Objects.requireNonNull(buffer);
- synchronized (mLock) {
- if (!mBufferRecords.contains(buffer.getId())) {
- Slogf.w(TAG_EVS, "Ignores a request to return a buffer with unknown id = "
- + buffer.getId());
- return;
- }
-
- mBufferRecords.remove(buffer.getId());
- }
-
- // This may throw a NullPointerException if the native EVS service handle is invalid.
- mHalWrapper.doneWithFrame(buffer.getId());
+ // 8 MSB tells the service type of this buffer.
+ @CarEvsServiceType int type = CarEvsUtils.getTag(buffer.getId());
+ mServiceInstances.get(type).doneWithFrame(buffer.getId());
}
/**
- * Returns a current status of CarEvsService.
+ * Returns a current status of CarEvsService's REARVIEW service type.
*
* <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
* access.
@@ -1065,7 +661,15 @@
public CarEvsStatus getCurrentStatus() {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
- return mStateEngine.getStateAndServiceType();
+ // This public API only returns current status of SERVICE_TYPE_REARVIEW. To get other
+ // services' status, please register a status listener via
+ // CarEvsService.registerStatusListener() API.
+ StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ if (instance == null) {
+ return null;
+ }
+
+ return instance.getCurrentStatus();
}
/**
@@ -1080,6 +684,10 @@
public IBinder generateSessionToken() {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY);
+ // TODO(b/191940626): With the unlimited multi-client supports, a validity of a session
+ // token does not make any different in handling streaming clients. This
+ // needs to be modified with a logic to manage the maximum number of
+ // streaming clients per service.
String systemUiPackageName = PackageManagerHelper.getSystemUiPackageName(mContext);
IBinder token = new Binder();
try {
@@ -1087,7 +695,7 @@
systemUiPackageName, UserHandle.SYSTEM.getIdentifier());
int callerUid = Binder.getCallingUid();
if (systemUiUid == callerUid) {
- setSessionToken(token);
+ mSessionTokens.add(token);
} else {
throw new SecurityException("SystemUI only can generate SessionToken");
}
@@ -1098,13 +706,6 @@
}
}
- private void handleClientDisconnected(ICarEvsStatusListener listener) {
- mStatusListeners.unregister(listener);
- if (mStatusListeners.getRegisteredCallbackCount() == 0) {
- Slogf.d(TAG_EVS, "Last status listener has been disconnected.");
- }
- }
-
/**
* Returns whether or not a given service type is supported.
*
@@ -1115,18 +716,12 @@
public boolean isSupported(@CarEvsServiceType int type) {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
- switch (type) {
- case CarEvsManager.SERVICE_TYPE_REARVIEW:
- return mHalWrapper.isConnected();
-
- case CarEvsManager.SERVICE_TYPE_SURROUNDVIEW:
- // TODO(b/179029031): Implements necessary logic when Surround View service is
- // integrated.
- return false;
-
- default:
- throw new IllegalArgumentException("Unknown service type = " + type);
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ return false;
}
+
+ return instance.isConnected();
}
/**
@@ -1144,18 +739,45 @@
if (!BuildHelper.isDebuggableBuild()) {
// This method is not allowed in the release build.
+ Slogf.e(TAG_EVS, "It is not allowed to change a camera assigned to the rearview " +
+ "in the release build.");
return false;
}
- if (id.equalsIgnoreCase(COMMAND_TO_USE_DEFAULT_CAMERA)) {
- mUseCameraIdOverride = false;
- Slogf.i(TAG_EVS, "CarEvsService is set to use the default device for the rearview.");
- } else {
- mCameraIdOverride = id;
- mUseCameraIdOverride = true;
- Slogf.i(TAG_EVS, "CarEvsService is set to use " + id + " for the rearview.");
+ mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW).setCameraId(id);
+ return true;
+ }
+
+ /**
+ * Sets a camera device for a given service type.
+ *
+ * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
+ *
+ * @param type A service type to assign a camera associated with a given string identifier.
+ * Please use '*' part of CarEvsManager.SERVICE_TYPE_* constants.
+ * @param id A string identifier of a target camera device.
+ * @return This method return true if it successfully programs a camera id for a given service
+ * type. Otherwise, this will return false.
+ */
+ public boolean setCameraIdFromCommand(@NonNull String type, @NonNull String id) {
+ CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
+
+ if (!BuildHelper.isDebuggableBuild()) {
+ // This method is not allowed in the release build.
+ Slogf.e(TAG_EVS, "It is not allowed to change a camera id assigned to the service " +
+ "in the release build.");
+ return false;
}
+ @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type);
+ StateMachine instance = mServiceInstances.get(serviceType);
+ if (instance == null) {
+ Slogf.e(TAG_EVS, "Ignores a request to set a camera %s for unavailable service %s.",
+ id, type);
+ return false;
+ }
+
+ instance.setCameraId(id);
return true;
}
@@ -1170,20 +792,126 @@
@NonNull
public String getRearviewCameraIdFromCommand() {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
- if (mUseCameraIdOverride) {
- return mCameraIdOverride;
- } else {
- return mContext.getString(R.string.config_evsRearviewCameraId);
+ return mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW).getCameraId();
+ }
+
+ /**
+ * Gets a String identifier of a camera assigned to a given service type.
+ *
+ * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
+ * access.
+ *
+ * @param type A service type to get a camera identifier. Please use "*" part of
+ * CarEvsManager.SERVICE_TYPE_* constants.
+ * @return A string identifier of a camera assigned to a given service type.
+ */
+ @Nullable
+ public String getCameraIdFromCommand(@NonNull String type) {
+ CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
+ @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type);
+ StateMachine instance = mServiceInstances.get(serviceType);
+ if (instance == null) {
+ return null;
}
+
+ return instance.getCameraId();
+ }
+
+ /**
+ * Enables a given service type with a specified camera device.
+ *
+ * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
+ *
+ * @param typeString A service type to get a camera identifier. Please use "*" part of
+ * CarEvsManager.SERVICE_TYPE_* constants.
+ * @param cameraId A string identifier of a target camera device. A camera associated with this
+ * id must not be assigned to any service type.
+ * @return false if a requested service type is already enabled or a specific camera id is
+ * already assigned to other service types.
+ * true otherwise.
+ */
+ public boolean enableServiceTypeFromCommand(@NonNull String typeString,
+ @NonNull String cameraId) {
+ CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
+
+ @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(typeString);
+ for (int i = 0; i < mServiceInstances.size(); i++) {
+ int type = mServiceInstances.keyAt(i);
+ StateMachine instance = mServiceInstances.valueAt(i);
+
+ if (type == serviceType || cameraId.equals(instance.getCameraId())) {
+ Slogf.e(TAG_EVS, "A requested service type is already provided by " +
+ " or a given camera id is used by %s.", instance);
+ return false;
+ }
+ }
+
+ StateMachine s = new StateMachine(mContext, mBuiltinContext, this, null,
+ serviceType, cameraId);
+ mServiceInstances.put(serviceType, s);
+ return true;
+ }
+
+ /**
+ * Checks whether or not a given service type is enabled.
+ *
+ * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
+ * access.
+ *
+ * @param type A service type to get a camera identifier. Please use "*" part of
+ * CarEvsManager.SERVICE_TYPE_* constants.
+ * @return true if a given service type is available.
+ * false otherwise.
+ */
+ public boolean isServiceTypeEnabledFromCommand(@NonNull String type) {
+ CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
+ @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type);
+ return mServiceInstances.get(serviceType) != null;
+ }
+
+ /** Checks whether or not a given token is valid. */
+ boolean isSessionToken(IBinder token) {
+ return mSessionTokens.contains(token);
+ }
+
+ /** Invalidate a given token. */
+ void invalidateSessionToken(IBinder token) {
+ mSessionTokens.remove(token);
+ }
+
+ /** Package-private version of generateSessionToken() method. */
+ @NonNull
+ IBinder generateSessionTokenInternal() {
+ IBinder token = new Binder();
+ mSessionTokens.add(token);
+ return token;
}
/**
* Manually sets a stream callback.
*/
@VisibleForTesting
- void setStreamCallback(@Nullable ICarEvsStreamCallback callback) {
+ void addStreamCallback(@CarEvsServiceType int type, @Nullable ICarEvsStreamCallback callback) {
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null || callback == null) {
+ return;
+ }
+
+ instance.addStreamCallback(callback);
+
+ ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder());
+ if (types == null) {
+ mCallbackToServiceType.put(callback.asBinder(),
+ new ArraySet<>(Set.of(new Integer(type))));
+ } else {
+ types.add(type);
+ }
+ }
+
+ /** Tells whether or not the latest EVS HAL event was requesting to start an activity. */
+ boolean needToStartActivity() {
synchronized (mLock) {
- mStreamCallback = callback;
+ return mLastEvsHalEvent != null && mLastEvsHalEvent.isRequestingToStartActivity();
}
}
@@ -1191,8 +919,13 @@
* Manually sets a current service state.
*/
@VisibleForTesting
- void setServiceState(@CarEvsServiceState int newState) {
- mStateEngine.setState(newState);
+ void setServiceState(@CarEvsServiceType int type, @CarEvsServiceState int newState) {
+ StateMachine instance = mServiceInstances.get(type);
+ if (instance == null) {
+ return;
+ }
+
+ instance.setState(newState);
}
/**
@@ -1217,96 +950,66 @@
private void handleClientDisconnected(ICarEvsStreamCallback callback) {
// If the last stream client is disconnected before it stops a video stream, request to stop
// current video stream.
- mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback);
+ ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder());
+ if (types == null) {
+ Slogf.d(TAG_EVS, "Ignores an incidental loss of unknown callback %s.",
+ callback.asBinder());
+ return;
+ }
+
+ for (int i = 0; i < types.size(); i++) {
+ StateMachine instance = mServiceInstances.get(types.valueAt(i));
+ if (instance == null) {
+ Slogf.i(TAG_EVS, "Ignores an incidental loss of a callback %s for service %d.",
+ callback.asBinder(), types.valueAt(i));
+ return;
+ }
+
+ instance.handleClientDisconnected(callback);
+ }
}
/** Notifies the service status gets changed */
- private void broadcastStateTransition(int type, int state) {
+ void broadcastStateTransition(int type, int state) {
int idx = mStatusListeners.beginBroadcast();
while (idx-- > 0) {
ICarEvsStatusListener listener = mStatusListeners.getBroadcastItem(idx);
try {
listener.onStatusChanged(new CarEvsStatus(type, state));
} catch (RemoteException e) {
- // Likely the binder death incident
+ // Likely the binder death incident.
Slogf.e(TAG_EVS, Log.getStackTraceString(e));
}
}
mStatusListeners.finishBroadcast();
}
- /** Starts a requested service */
- private boolean startService(@CarEvsServiceType int type) {
- if (type == CarEvsManager.SERVICE_TYPE_SURROUNDVIEW) {
- // TODO(b/179029031): Removes below when Surround View service is integrated.
- Slogf.e(TAG_EVS, "Surround view is not supported yet.");
- return false;
- }
-
- if (!mHalWrapper.connectToHalServiceIfNecessary()) {
- Slogf.e(TAG_EVS, "Failed to connect to EVS service");
- return false;
- }
-
- String cameraId;
- if (mUseCameraIdOverride) {
- cameraId = mCameraIdOverride;
- } else {
- cameraId = mContext.getString(R.string.config_evsRearviewCameraId);
- }
-
- if (!mHalWrapper.openCamera(cameraId)) {
- Slogf.e(TAG_EVS, "Failed to open a target camera device");
- return false;
- }
-
- return true;
- }
-
/** Stops a current service */
- private void stopService() {
+ void stopService() {
stopService(/* callback= */ null);
}
private void stopService(ICarEvsStreamCallback callback) {
- try {
- synchronized (mLock) {
- if (callback != null && callback.asBinder() != mStreamCallback.asBinder()) {
- Slogf.w(TAG_EVS, "Decline a request to stop a video from an unknown client.");
- return;
- }
+ ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder());
+ if (types == null || types.isEmpty()) {
+ Slogf.d(TAG_EVS, "Ignores a request to stop a service for unknown callback %s.",
+ callback.asBinder());
+ return;
+ }
- unlinkToDeathStreamCallbackLocked();
- mStreamCallback = null;
- }
- Slogf.i(TAG_EVS, "Last stream client has been disconnected.");
-
- // Notify the client that the stream has ended.
- if (callback != null) {
- notifyStreamStopped(callback);
+ for (int i = 0; i < types.size(); i++) {
+ StateMachine instance = mServiceInstances.get(types.valueAt(i));
+ if (instance == null) {
+ Slogf.i(TAG_EVS, "Ignores a request to stop unsupported service %d.",
+ types.valueAt(i));
+ return;
}
- // Request to stop a video stream if it is active.
- mHalWrapper.requestToStopVideoStream();
- } catch (RuntimeException e) {
- Slogf.w(TAG_EVS, Log.getStackTraceString(e));
- } finally {
- // We simply drop all buffer records; the native method will return all pending buffers
- // to the native Extended System View service if it is alive.
- synchronized (mBufferRecords) {
- mBufferRecords.clear();
- }
-
- // Cancel a pending message to check a request timeout
- mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
-
- // Close current camera
- mHalWrapper.closeCamera();
+ instance.requestStopVideoStream(callback);
}
}
- @GuardedBy("mLock")
- private void handlePropertyEventLocked(CarPropertyEvent event) {
+ private void handlePropertyEvent(CarPropertyEvent event) {
if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
// CarEvsService is interested only in the property change event.
return;
@@ -1319,125 +1022,45 @@
}
long timestamp = value.getTimestamp();
- if (timestamp != 0 && timestamp <= mLastEvsHalEvent.getTimestamp()) {
- if (DBG) {
- Slogf.d(TAG_EVS,
- "Ignoring GEAR_SELECTION change happened past, timestamp = " + timestamp +
- ", last event was at " + mLastEvsHalEvent.getTimestamp());
+ boolean isReverseGear;
+ synchronized (mLock) {
+ if (timestamp != 0 && timestamp <= mLastEvsHalEvent.getTimestamp()) {
+ if (DBG) {
+ Slogf.d(TAG_EVS,
+ "Ignoring GEAR_SELECTION change happened past, timestamp = " +
+ timestamp + ", last event was at " + mLastEvsHalEvent.getTimestamp());
+ }
+ return;
}
+
+
+ isReverseGear = (Integer) value.getValue() == VehicleGear.GEAR_REVERSE;
+ mLastEvsHalEvent = new EvsHalEvent(timestamp, CarEvsManager.SERVICE_TYPE_REARVIEW,
+ isReverseGear);
+ }
+
+ StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ if (instance == null) {
+ Slogf.i(TAG_EVS,
+ "Ignore a GEAR_SELECTION event because the rearview service is not available.");
return;
}
-
- boolean isReverseGear = (Integer) value.getValue() == VehicleGear.GEAR_REVERSE;
- mLastEvsHalEvent = new EvsHalEvent(timestamp, CarEvsManager.SERVICE_TYPE_REARVIEW,
- isReverseGear);
-
- if (mStateEngine.getState() == SERVICE_STATE_UNAVAILABLE) {
- return;
- }
-
- // TODO(b/179029031): CarEvsService may need to process VehicleGear.GEAR_PARK when
- // Surround View service is integrated.
if (isReverseGear) {
// Request to start the rearview activity when the gear is shifted into the reverse
// position.
- if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_REQUESTED,
- CarEvsManager.SERVICE_TYPE_REARVIEW) != ERROR_NONE) {
+ if (instance.requestStartActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) {
Slogf.w(TAG_EVS, "Failed to request the rearview activity.");
}
} else {
// Request to stop the rearview activity when the gear is shifted from the reverse
// position to other positions.
- if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE,
- CarEvsManager.SERVICE_TYPE_REARVIEW, /* token = */ null, mStreamCallback)
- != ERROR_NONE) {
- Slogf.d(TAG_EVS, "Failed to stop the rearview activity.");
+ if (instance.requestStopActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) {
+ Slogf.i(TAG_EVS, "Failed to stop the rearview activity.");
}
}
}
- /** Processes a streaming event and propagates it to registered clients */
- private void processStreamEvent(@CarEvsStreamEvent int event) {
- synchronized (mLock) {
- if (mStreamCallback == null) {
- return;
- }
-
- try {
- mStreamCallback.onStreamEvent(event);
- } catch (RemoteException e) {
- // Likely the binder death incident
- Slogf.e(TAG_EVS, Log.getStackTraceString(e));
- }
- }
- }
-
- /**
- * Processes a streaming event and propagates it to registered clients.
- *
- * @return True if this buffer is hold and used by the client, false otherwise.
- */
- private boolean processNewFrame(int id, @NonNull HardwareBuffer buffer) {
- Objects.requireNonNull(buffer);
-
- synchronized (mLock) {
- if (mStreamCallback == null) {
- return false;
- }
-
- try {
- mStreamCallback.onNewFrame(new CarEvsBufferDescriptor(id, buffer));
- mBufferRecords.add(id);
- } catch (RemoteException e) {
- // Likely the binder death incident
- Slogf.e(TAG_EVS, Log.getStackTraceString(e));
- return false;
- }
- }
-
- return true;
- }
-
- /** EVS stream event handler called after a native handler */
- @Override
- public void onHalEvent(int eventType) {
- processStreamEvent(
- CarEvsServiceUtils.convertToStreamEvent(eventType));
- }
-
- /** EVS frame handler called after a native handler */
- @Override
- public void onFrameEvent(int id, HardwareBuffer buffer) {
- try {
- if (!processNewFrame(id, buffer)) {
- // No client uses this buffer.
- Slogf.d(TAG_EVS, "Returns buffer " + id + " because no client uses it.");
- mHalWrapper.doneWithFrame(id);
- }
- } finally {
- buffer.close();
- }
- }
-
- /** EVS service death handler called after a native handler */
- @Override
- public void onHalDeath() {
- // We have lost the Extended View System service.
- mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_UNAVAILABLE);
- connectToHalServiceIfNecessary(EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS);
- }
-
- /** Try to connect to the EVS HAL service until it succeeds at a given interval */
- private void connectToHalServiceIfNecessary(long intervalInMillis) {
- Slogf.d(TAG_EVS, "Trying to connect to the EVS HAL service.");
- if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE) != ERROR_NONE) {
- // Try to restore a connection again after a given amount of time
- mHandler.postDelayed(() -> connectToHalServiceIfNecessary(intervalInMillis),
- intervalInMillis);
- }
- }
-
/** Notify the client of a video stream loss */
private static void notifyStreamStopped(@NonNull ICarEvsStreamCallback callback) {
Objects.requireNonNull(callback);
@@ -1449,4 +1072,12 @@
Slogf.w(TAG_EVS, Log.getStackTraceString(e));
}
}
+
+ /** Handles a disconnection of a status monitoring client. */
+ private void handleClientDisconnected(ICarEvsStatusListener listener) {
+ mStatusListeners.unregister(listener);
+ if (mStatusListeners.getRegisteredCallbackCount() == 0) {
+ Slogf.d(TAG_EVS, "Last status listener has been disconnected.");
+ }
+ }
}
diff --git a/service/src/com/android/car/evs/CarEvsServiceUtils.java b/service/src/com/android/car/evs/CarEvsServiceUtils.java
index 5b3b844..8e850ce 100644
--- a/service/src/com/android/car/evs/CarEvsServiceUtils.java
+++ b/service/src/com/android/car/evs/CarEvsServiceUtils.java
@@ -15,11 +15,25 @@
*/
package com.android.car.evs;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_DRIVERVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_USER_DEFINED;
+
import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.car.builtin.util.Slogf;
import android.car.evs.CarEvsManager;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
import android.car.evs.CarEvsManager.CarEvsStreamEvent;
+import com.android.car.internal.evs.CarEvsUtils;
+
/**
* Utility class for CarEvsService
*
@@ -28,6 +42,11 @@
@SystemApi
public final class CarEvsServiceUtils {
private static final String TAG = CarEvsServiceUtils.class.getSimpleName();
+
+ private static final int INVALID_SERVICE_TYPE = -1;
+
+ private static String INVALID_CAMERA_ID = "";
+
private CarEvsServiceUtils() {}
/**
@@ -67,4 +86,75 @@
return outputStatus;
}
+
+ final static class Parameters {
+ private final @CarEvsServiceType int mServiceType;
+ private final ComponentName mActivityName;
+ private String mCameraId;
+
+ private Parameters(int type, String cameraId, String activityName) {
+ mServiceType = type;
+ mCameraId = cameraId;
+ if (activityName != null && !activityName.isEmpty()) {
+ mActivityName = ComponentName.unflattenFromString(activityName);
+ } else {
+ mActivityName = null;
+ }
+ }
+
+ static Parameters create(int type, String cameraId, String activityName) {
+ return new Parameters(type, cameraId, activityName);
+ }
+
+ @CarEvsServiceType int getType() { return mServiceType; }
+ String getCameraId() { return mCameraId; }
+ void setCameraId(String cameraId) { mCameraId = cameraId; }
+ ComponentName getActivityComponentName() { return mActivityName; }
+
+ @Override
+ public String toString() {
+ return "Parameter serviceType=" + CarEvsUtils.convertToString(mServiceType) +
+ ", cameraId=" + mCameraId + ", activityName=" + mActivityName;
+ }
+ }
+
+ static Parameters parse(String rawString) {
+ @CarEvsServiceType int serviceType = INVALID_SERVICE_TYPE;
+ String activityName = null;
+ String cameraId = INVALID_CAMERA_ID;
+
+ // Example a service configuration string:
+ // <item>serviceType=REARVIEW,cameraId=/dev/video0,
+ // activityName=com.android.car/com.google.android.car.evs.CarEvsCameraPreviewActivity
+ // </item>
+ // <item>serviceType=FRONTVIEW,cameraId=/dev/video1</item>
+ String[] tokens = rawString.split(",");
+ for (String token : tokens) {
+ String[] keyValuePair = token.split("=");
+ if (keyValuePair.length != 2) {
+ Slogf.w(TAG, "Skip a key-value pair in incorrect format, " + token);
+ continue;
+ }
+
+ switch (keyValuePair[0]) {
+ case "serviceType":
+ serviceType = CarEvsUtils.convertToServiceType(keyValuePair[1]);
+ break;
+
+ case "cameraId":
+ cameraId = keyValuePair[1];
+ break;
+
+ case "activityName":
+ activityName = keyValuePair[1];
+ break;
+
+ default:
+ Slogf.e(TAG, "Unknown parameter: " + token);
+ break;
+ }
+ }
+
+ return Parameters.create(serviceType, cameraId, activityName);
+ }
}
diff --git a/service/src/com/android/car/evs/StateMachine.java b/service/src/com/android/car/evs/StateMachine.java
new file mode 100644
index 0000000..a9dbeb5
--- /dev/null
+++ b/service/src/com/android/car/evs/StateMachine.java
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.evs;
+
+import static android.car.evs.CarEvsManager.ERROR_BUSY;
+import static android.car.evs.CarEvsManager.ERROR_NONE;
+import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_ACTIVE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_INACTIVE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_REQUESTED;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_UNAVAILABLE;
+import static android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED;
+
+import static com.android.car.CarLog.TAG_EVS;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.builtin.util.Slogf;
+import android.car.evs.CarEvsBufferDescriptor;
+import android.car.evs.CarEvsManager;
+import android.car.evs.CarEvsManager.CarEvsError;
+import android.car.evs.CarEvsManager.CarEvsServiceState;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
+import android.car.evs.CarEvsManager.CarEvsStreamEvent;
+import android.car.evs.CarEvsStatus;
+import android.car.evs.ICarEvsStreamCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.HardwareBuffer;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.SparseIntArray;
+import android.util.Log;
+
+import com.android.car.BuiltinPackageDependency;
+import com.android.car.CarServiceUtils;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+import com.android.car.internal.evs.CarEvsUtils;
+import com.android.car.internal.evs.EvsHalWrapper;
+import com.android.car.internal.util.IndentingPrintWriter;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.util.Objects;
+
+/** CarEvsService state machine implementation to handle all state transitions. */
+final class StateMachine {
+ // Service request priorities
+ static final int REQUEST_PRIORITY_LOW = 0;
+ static final int REQUEST_PRIORITY_NORMAL = 1;
+ static final int REQUEST_PRIORITY_HIGH = 2;
+
+ // Timeout for a request to start a video stream with a valid token.
+ private static final int STREAM_START_REQUEST_TIMEOUT_MS = 3000;
+
+ private static final boolean DBG = Slogf.isLoggable(TAG_EVS, Log.DEBUG);
+
+ // Interval for connecting to the EVS HAL service trial.
+ private static final long EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS = 1000;
+ // Object to recognize Runnable objects.
+ private static final String CALLBACK_RUNNABLE_TOKEN = StateMachine.class.getSimpleName();
+ private static final String DEFAULT_CAMERA_ALIAS = "default";
+
+ private final SparseIntArray mBufferRecords = new SparseIntArray();
+ private final CarEvsService mService;
+ private final ComponentName mActivityName;
+ private final Context mContext;
+ private final EvsHalWrapper mHalWrapper;
+ private final HalCallback mHalCallback;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread =
+ CarServiceUtils.getHandlerThread(getClass().getSimpleName());
+ private final Object mLock = new Object();
+ private final Runnable mActivityRequestTimeoutRunnable = () -> handleActivityRequestTimeout();
+ private final String mLogTag;
+ private final @CarEvsServiceType int mServiceType;
+
+ private final class StreamCallbackList extends RemoteCallbackList<ICarEvsStreamCallback> {
+ @Override
+ public void onCallbackDied(ICarEvsStreamCallback callback) {
+ if (callback == null) {
+ return;
+ }
+
+ Slogf.w(mLogTag, "StreamCallback %s has died.", callback.asBinder());
+ synchronized (mLock) {
+ if (StateMachine.this.needToStartActivityLocked()) {
+ if (StateMachine.this.startActivity(/* resetState= */ true) != ERROR_NONE) {
+ Slogf.e(mLogTag, "Failed to request the acticity.");
+ }
+ } else {
+ // Ensure we stops streaming.
+ StateMachine.this.handleClientDisconnected(callback);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private String mCameraId;
+
+ // Current state.
+ @GuardedBy("mLock")
+ private int mState = SERVICE_STATE_UNAVAILABLE;
+
+ // Priority of a last service request.
+ @GuardedBy("mLock")
+ private int mLastRequestPriority = REQUEST_PRIORITY_LOW;
+
+ // The latest session token issued to the privileged client.
+ @GuardedBy("mLock")
+ private IBinder mSessionToken = null;
+
+ // A callback associated with current session token.
+ @GuardedBy("mLock")
+ private ICarEvsStreamCallback mPrivilegedCallback;
+
+ // This is a device name to override initial camera id.
+ private String mCameraIdOverride = null;
+
+ @VisibleForTesting
+ final class HalCallback implements EvsHalWrapper.HalEventCallback {
+
+ private final StreamCallbackList mCallbacks = new StreamCallbackList();
+
+ /** EVS stream event handler called after a native handler. */
+ @Override
+ public void onHalEvent(int event) {
+ mHandler.postDelayed(() -> processStreamEvent(event),
+ CALLBACK_RUNNABLE_TOKEN, /* delayMillis= */ 0);
+ }
+
+ /** EVS frame handler called after a native handler. */
+ @Override
+ public void onFrameEvent(int id, HardwareBuffer buffer) {
+ mHandler.postDelayed(() -> processNewFrame(id, buffer),
+ CALLBACK_RUNNABLE_TOKEN, /* delayMillis= */ 0);
+ }
+
+ /** EVS service death handler called after a native handler. */
+ @Override
+ public void onHalDeath() {
+ // We have lost the Extended View System service.
+ execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_UNAVAILABLE);
+ connectToHalServiceIfNecessary(EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS);
+ }
+
+ boolean register(ICarEvsStreamCallback callback, IBinder token) {
+ return mCallbacks.register(callback, token);
+ }
+
+ boolean unregister(ICarEvsStreamCallback callback) {
+ return mCallbacks.unregister(callback);
+ }
+
+ boolean contains(ICarEvsStreamCallback target) {
+ boolean found = false;
+ synchronized (mCallbacks) {
+ int idx = mCallbacks.beginBroadcast();
+ while (!found && idx-- > 0) {
+ ICarEvsStreamCallback callback = mCallbacks.getBroadcastItem(idx);
+ found = target.asBinder() == callback.asBinder();
+ }
+ mCallbacks.finishBroadcast();
+ }
+ return found;
+ }
+
+ boolean isEmpty() {
+ return mCallbacks.getRegisteredCallbackCount() == 0;
+ }
+
+ RemoteCallbackList get() {
+ return mCallbacks;
+ }
+
+ int size() {
+ return mCallbacks.getRegisteredCallbackCount();
+ }
+
+ void stop() {
+ synchronized (mCallbacks) {
+ int idx = mCallbacks.beginBroadcast();
+ while (idx-- > 0) {
+ ICarEvsStreamCallback callback = mCallbacks.getBroadcastItem(idx);
+ requestStopVideoStream(callback);
+ }
+ mCallbacks.finishBroadcast();
+ }
+ }
+
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+ void dump(IndentingPrintWriter writer) {
+ writer.printf("Active clients:\n");
+ writer.increaseIndent();
+ synchronized (mCallbacks) {
+ int idx = mCallbacks.beginBroadcast();
+ while (idx-- > 0) {
+ writer.printf("%s\n", mCallbacks.getBroadcastItem(idx).asBinder());
+ }
+ mCallbacks.finishBroadcast();
+ }
+ writer.decreaseIndent();
+ }
+
+ /** Processes a streaming event and propagates it to registered clients */
+ private void processStreamEvent(@CarEvsStreamEvent int event) {
+ synchronized (mCallbacks) {
+ int idx = mCallbacks.beginBroadcast();
+ while (idx-- > 0) {
+ ICarEvsStreamCallback callback = mCallbacks.getBroadcastItem(idx);
+ try {
+ int taggedEvent = CarEvsUtils.putTag(mServiceType, event);
+ callback.onStreamEvent(taggedEvent);
+ } catch (RemoteException e) {
+ Slogf.w(mLogTag, "Failed to forward an event to %s", callback);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+ }
+
+ /**
+ * Processes a streaming event and propagates it to registered clients.
+ *
+ * @return Number of successful callbacks.
+ */
+ private int processNewFrame(int id, @NonNull HardwareBuffer buffer) {
+ Objects.requireNonNull(buffer);
+
+ // Counts how many callbacks are successfully done.
+ int refcount = 0;
+ synchronized (mCallbacks) {
+ int idx = mCallbacks.beginBroadcast();
+ while (idx-- > 0) {
+ ICarEvsStreamCallback callback = mCallbacks.getBroadcastItem(idx);
+ try {
+ int bufferId = CarEvsUtils.putTag(mServiceType, id);
+ callback.onNewFrame(new CarEvsBufferDescriptor(bufferId, buffer));
+ refcount += 1;
+ } catch (RemoteException e) {
+ Slogf.w(mLogTag, "Failed to forward a frame to %s", callback);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+ buffer.close();
+
+ if (refcount > 0) {
+ synchronized (mLock) {
+ mBufferRecords.put(id, refcount);
+ }
+ } else {
+ Slogf.i(mLogTag, "No client is actively listening.");
+ mHalWrapper.doneWithFrame(id);
+ }
+ return refcount;
+ }
+ }
+
+ @VisibleForTesting
+ static EvsHalWrapper createHalWrapper(Context builtinContext,
+ EvsHalWrapper.HalEventCallback callback) {
+ try {
+ Class helperClass = builtinContext.getClassLoader().loadClass(
+ BuiltinPackageDependency.EVS_HAL_WRAPPER_CLASS);
+ Constructor constructor = helperClass.getConstructor(
+ new Class[]{EvsHalWrapper.HalEventCallback.class});
+ return (EvsHalWrapper) constructor.newInstance(callback);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Cannot load class:" + BuiltinPackageDependency.EVS_HAL_WRAPPER_CLASS, e);
+ }
+ }
+
+ // Constructor
+ StateMachine(Context context, Context builtinContext, CarEvsService service,
+ ComponentName activityName, @CarEvsServiceType int type, String cameraId) {
+ this(context, builtinContext, service, activityName, type, cameraId, /* handler= */ null);
+ }
+
+ StateMachine(Context context, Context builtinContext, CarEvsService service,
+ ComponentName activityName, @CarEvsServiceType int type, String cameraId,
+ Handler handler) {
+ String postfix = "." + CarEvsUtils.convertToString(type);
+ mLogTag = TAG_EVS + postfix;
+ mContext = context;
+ mCameraId = cameraId;
+ mActivityName = activityName;
+ if (DBG) {
+ Slogf.d(mLogTag, "Camera Activity=%s", mActivityName);
+ }
+
+ if (handler == null) {
+ mHandler = new Handler(mHandlerThread.getLooper());
+ } else {
+ mHandler = handler;
+ }
+
+ mHalCallback = new HalCallback();
+ mHalWrapper = StateMachine.createHalWrapper(builtinContext, mHalCallback);
+ mService = service;
+ mServiceType = type;
+ }
+
+ /***** Visible instance method section. *****/
+
+ /** Initializes this StateMachine instance. */
+ boolean init() {
+ return mHalWrapper.init();
+ }
+
+ /** Releases this StateMachine instance. */
+ void release() {
+ mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
+ mHalWrapper.release();
+ }
+
+ /**
+ * Checks whether we are connected to the native EVS service.
+ *
+ * @return true if our connection to the native EVS service is valid.
+ * false otherwise.
+ */
+ boolean isConnected() {
+ return mHalWrapper.isConnected();
+ }
+
+ /**
+ * Sets a string camera identifier to use.
+ *
+ * @param id A string identifier of a target camera device.
+ */
+ void setCameraId(String id) {
+ if (id.equalsIgnoreCase(DEFAULT_CAMERA_ALIAS)) {
+ mCameraIdOverride = mCameraId;
+ Slogf.i(TAG_EVS, "CarEvsService is set to use the default device for the rearview.");
+ } else {
+ mCameraIdOverride = id;
+ Slogf.i(TAG_EVS, "CarEvsService is set to use " + id + " for the rearview.");
+ }
+ }
+
+ /**
+ * Sets a string camera identifier to use.
+ *
+ * @return A camera identifier string we're going to use.
+ */
+ String getCameraId() {
+ return mCameraIdOverride != null ? mCameraIdOverride : mCameraId;
+ }
+
+ /**
+ * Notifies that we're done with a frame buffer associated with a given identifier.
+ *
+ * @param id An identifier of a frame buffer we have consumed.
+ */
+ void doneWithFrame(int id) {
+ int bufferId = CarEvsUtils.getValue(id);
+ synchronized (mLock) {
+ int refcount = mBufferRecords.get(bufferId) - 1;
+ if (refcount > 0) {
+ if (DBG) {
+ Slogf.d(mLogTag, "Buffer %d has %d references.", id, refcount);
+ }
+ mBufferRecords.put(bufferId, refcount);
+ return;
+ }
+
+ mBufferRecords.delete(bufferId);
+ }
+
+ // This may throw a NullPointerException if the native EVS service handle is invalid.
+ mHalWrapper.doneWithFrame(bufferId);
+ }
+
+ /**
+ * Requests to start a registered activity with a given priority.
+ *
+ * @param priority A priority of current request; this should be either REQUEST_PRIORITY_HIGH or
+ * REQUEST_PRIORITY_NORMAL.
+ *
+ * @return ERROR_UNEVAILABLE if we are not initialized yet or we failed to connect to the native
+ * EVS service.
+ * ERROR_BUSY if a pending request has a higher priority.
+ * ERROR_NONE if no activity is registered or we succeed to request a registered
+ * activity.
+ */
+ @CarEvsError int requestStartActivity(int priority) {
+ if (mContext == null) {
+ Slogf.e(mLogTag, "Context is not valid.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ if (mActivityName == null) {
+ Slogf.d(mLogTag, "No activity is set.");
+ return ERROR_NONE;
+ }
+
+ return execute(priority, SERVICE_STATE_REQUESTED);
+ }
+
+ /**
+ * Requests to start a registered activity if it is necessary.
+ *
+ * @return ERROR_UNEVAILABLE if we are not initialized yet or we failed to connect to the native
+ * EVS service.
+ * ERROR_BUSY if a pending request has a higher priority.
+ * ERROR_NONE if no activity is registered or we succeed to request a registered
+ * activity.
+ */
+ @CarEvsError int requestStartActivityIfNecessary() {
+ return startActivityIfNecessary();
+ }
+
+ /**
+ * Requests to stop an activity.
+ *
+ * @param priority A priority of current request; this should be either REQUEST_PRIORITY_HIGH or
+ * REQUEST_PRIORITY_NORMAL.
+ *
+ * @return ERROR_NONE if no active streaming client exists, no activity has been registered, or
+ * current activity is successfully stopped.
+ * ERROR_UNAVAILABLE if we cannot connect to the native EVS service.
+ * ERROR_BUSY if current activity has a higher priority than a given priority.
+ */
+ @CarEvsError int requestStopActivity(int priority) {
+ if (mActivityName == null) {
+ Slogf.d(mLogTag, "Ignore a request to stop activity mActivityName=%s", mActivityName);
+ return ERROR_NONE;
+ }
+
+ stopActivity();
+ return ERROR_NONE;
+ }
+
+ /** Requests to cancel a pending activity request. */
+ void cancelActivityRequest() {
+ if (mState != SERVICE_STATE_REQUESTED) {
+ return;
+ }
+
+ if (execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE) != ERROR_NONE) {
+ Slogf.w(mLogTag, "Failed to transition to INACTIVE state.");
+ }
+ }
+
+ /** Tries to connect to the EVS HAL service until it succeeds at a default interval. */
+ void connectToHalServiceIfNecessary() {
+ connectToHalServiceIfNecessary(EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS);
+ }
+
+ /** Shuts down the service and enters INACTIVE state. */
+ void stopService() {
+ // Stop all active clients.
+ mHalCallback.stop();
+ }
+
+ /**
+ * Prioritizes video stream request and start a video stream.
+ *
+ * @param callback A callback to get frame buffers and stream events.
+ * @param token A token to recognize a client. If this is a valid session token, its owner will
+ * prioritized.
+ *
+ * @return ERROR_UNAVAILABLE if we're not connected to the native EVS service.
+ * ERROR_BUSY if current client has a higher priority.
+ * ERROR_NONE otherwise.
+ */
+ @CarEvsError int requestStartVideoStream(ICarEvsStreamCallback callback, IBinder token) {
+ int priority;
+ if (isSessionToken(token)) {
+ // If a current request has a valid session token, we assume it comes from an activity
+ // launched by us for the high priority request.
+ mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
+ priority = REQUEST_PRIORITY_HIGH;
+ } else {
+ priority = REQUEST_PRIORITY_LOW;
+ }
+
+ return execute(priority, SERVICE_STATE_ACTIVE, token, callback);
+ }
+
+ /**
+ * Stops a video stream.
+ *
+ * @param callback A callback client who want to stop listening.
+ */
+ void requestStopVideoStream(ICarEvsStreamCallback callback) {
+ if (!mHalCallback.contains(callback)) {
+ Slogf.d(mLogTag, "Ignores a video stream stop request not from current stream client.");
+ return;
+ }
+
+ if (execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback) != ERROR_NONE) {
+ Slogf.w(mLogTag, "Failed to stop a video stream");
+ }
+ }
+
+ /**
+ * Gets a current status of StateMachine.
+ *
+ * @return CarEvsServiceState that describes current state of a StateMachine instance.
+ */
+ CarEvsStatus getCurrentStatus() {
+ synchronized (mLock) {
+ return new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW, mState);
+ }
+ }
+
+ /**
+ * Returns a String that describes a current session token.
+ */
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+ void dump(IndentingPrintWriter writer) {
+ synchronized (mLock) {
+ writer.printf("StateMachine 0x%s is providing %s.\n",
+ Integer.toHexString(System.identityHashCode(this)),
+ CarEvsUtils.convertToString(mServiceType));
+ writer.printf("SessionToken = %s.\n",
+ mSessionToken == null ? "Not exist" : mSessionToken);
+ writer.increaseIndent();
+ mHalCallback.dump(writer);
+ writer.decreaseIndent();
+ writer.printf("\n");
+ }
+ }
+
+ /**
+ * Confirms whether a given IBinder object is identical to current session token IBinder object.
+ *
+ * @param token IBinder object that a caller wants to examine.
+ *
+ * @return true if a given IBinder object is a valid session token.
+ * false otherwise.
+ */
+ boolean isSessionToken(IBinder token) {
+ synchronized (mLock) {
+ return isSessionTokenLocked(token);
+ }
+ }
+
+ /** Handles client disconnections; may request to stop a video stream. */
+ void handleClientDisconnected(ICarEvsStreamCallback callback) {
+ // If the last stream client is disconnected before it stops a video stream, request to stop
+ // current video stream.
+ execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback);
+ }
+
+ /************************** Private methods ************************/
+
+ private @CarEvsError int execute(int priority, int destination) {
+ return execute(priority, destination, null, null);
+ }
+
+
+ private @CarEvsError int execute(int priority, int destination,
+ ICarEvsStreamCallback callback) {
+ return execute(priority, destination, null, callback);
+ }
+
+ /**
+ * Executes StateMachine to be in a requested state.
+ *
+ * @param priority A priority of current execution.
+ * @param destination A target service state we're desired to enter.
+ * @param token A session token IBinder object.
+ * @param callback A callback object we may need to work with.
+ *
+ * @return ERROR_NONE if we're already in a requested state.
+ * CarEvsError from each handler methods.
+ */
+ private @CarEvsError int execute(int priority, int destination, IBinder token,
+ ICarEvsStreamCallback callback) {
+ int result = ERROR_NONE;
+ int previousState, newState;
+ synchronized (mLock) {
+ previousState = mState;
+ Slogf.i(mLogTag, "Transition requested: %s -> %s", stateToString(previousState),
+ stateToString(destination));
+ switch (destination) {
+ case SERVICE_STATE_UNAVAILABLE:
+ result = handleTransitionToUnavailableLocked();
+ break;
+
+ case SERVICE_STATE_INACTIVE:
+ result = handleTransitionToInactiveLocked(priority, callback);
+ break;
+
+ case SERVICE_STATE_REQUESTED:
+ result = handleTransitionToRequestedLocked(priority);
+ break;
+
+ case SERVICE_STATE_ACTIVE:
+ result = handleTransitionToActiveLocked(priority, token, callback);
+ break;
+
+ default:
+ throw new IllegalStateException(
+ "CarEvsService is in the unknown state, " + previousState);
+ }
+
+ newState = mState;
+ }
+
+ if (result == ERROR_NONE) {
+ if (previousState != newState) {
+ Slogf.i(mLogTag, "Transition completed: %s", stateToString(destination));
+ mService.broadcastStateTransition(CarEvsManager.SERVICE_TYPE_REARVIEW, newState);
+ } else {
+ Slogf.i(mLogTag, "Stay at %s", stateToString(newState));
+ }
+ } else {
+ Slogf.e(mLogTag, "Transition failed: error = %d", result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks conditions and tells whether we need to launch a registered activity.
+ *
+ * @return true if we should launch an activity.
+ * false otherwise.
+ */
+ private boolean needToStartActivity() {
+ if (mActivityName == null || mHandler.hasCallbacks(mActivityRequestTimeoutRunnable)) {
+ // No activity has been registered yet or it is already requested.
+ Slogf.d(mLogTag,
+ "No need to start an activity: mActivityName=%s, mHandler.hasCallbacks()=%s",
+ mActivityName, mHandler.hasCallbacks(mActivityRequestTimeoutRunnable));
+ return false;
+ }
+
+ boolean startActivity = mService.needToStartActivity();
+ synchronized (mLock) {
+ startActivity |= checkCurrentStateRequiresSystemActivityLocked();
+ }
+
+ return startActivity;
+ }
+
+ /**
+ * Checks conditions and tells whether we need to launch a registered activity.
+ *
+ * @return true if we should launch an activity.
+ * false otherwise.
+ */
+ private boolean needToStartActivityLocked() {
+ if (mActivityName == null || mHandler.hasCallbacks(mActivityRequestTimeoutRunnable)) {
+ // No activity has been registered yet or it is already requested.
+ Slogf.d(mLogTag,
+ "No need to start an activity: mActivityName=%s, mHandler.hasCallbacks()=%s",
+ mActivityName, mHandler.hasCallbacks(mActivityRequestTimeoutRunnable));
+ return false;
+ }
+
+ return mService.needToStartActivity() || checkCurrentStateRequiresSystemActivityLocked();
+ }
+
+ /**
+ * Launches a registered camera activity if necessary.
+ *
+ * @return ERROR_UNEVAILABLE if we are not initialized yet or we failed to connect to the native
+ * EVS service.
+ * ERROR_BUSY if a pending request has a higher priority.
+ * ERROR_NONE if no activity is registered or we succeed to request a registered
+ * activity.
+ */
+ private @CarEvsError int startActivityIfNecessary() {
+ return startActivityIfNecessary(/* resetState= */ false);
+ }
+
+ /**
+ * Launches a registered activity if necessary.
+ *
+ * @param resetState when this is true, StateMachine enters INACTIVE state first and then moves
+ * into REQUESTED state.
+ *
+ * @return ERROR_UNEVAILABLE if we are not initialized yet or we failed to connect to the native
+ * EVS service.
+ * ERROR_BUSY if a pending request has a higher priority.
+ * ERROR_NONE if no activity is registered or we succeed to request a registered
+ * activity.
+ */
+ private @CarEvsError int startActivityIfNecessary(boolean resetState) {
+ if (!needToStartActivity()) {
+ // We do not need to start a camera activity.
+ return ERROR_NONE;
+ }
+
+ return startActivity(resetState);
+ }
+
+ /**
+ * Launches a registered activity.
+ *
+ * @param resetState when this is true, StateMachine enters INACTIVE state first and then moves
+ * into REQUESTED state.
+ *
+ * @return ERROR_UNEVAILABLE if we are not initialized yet or we failed to connect to the native
+ * EVS service.
+ * ERROR_BUSY if a pending request has a higher priority.
+ * ERROR_NONE if no activity is registered or we succeed to request a registered
+ * activity.
+ */
+ private @CarEvsError int startActivity(boolean resetState) {
+ // Request to launch an activity again after cleaning up.
+ int result = ERROR_NONE;
+ if (resetState) {
+ result = execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE);
+ if (result != ERROR_NONE) {
+ return result;
+ }
+ }
+
+ return execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_REQUESTED);
+ }
+
+ /** Stops a registered activity if it's running and enters INACTIVE state. */
+ private void stopActivity() {
+ IBinder token;
+ ICarEvsStreamCallback callback;
+ synchronized (mLock) {
+ token = mSessionToken;
+ callback = mPrivilegedCallback;
+ }
+
+ if (token == null || callback == null) {
+ Slogf.d(mLogTag, "No activity is running.");
+ return;
+ }
+
+ if (execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback) != ERROR_NONE) {
+ Slogf.w(mLogTag, "Failed to stop a video stream");
+ }
+ }
+
+ /**
+ * Try to connect to the EVS HAL service until it succeeds at a given interval.
+ *
+ * @param internalInMillis an interval to try again if current attempt fails.
+ */
+ private void connectToHalServiceIfNecessary(long intervalInMillis) {
+ if (execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE) != ERROR_NONE) {
+ // Try to restore a connection again after a given amount of time.
+ Slogf.i(TAG_EVS, "Failed to connect to EvsManager service. Retrying after %d ms.",
+ intervalInMillis);
+ mHandler.postDelayed(() -> connectToHalServiceIfNecessary(intervalInMillis),
+ intervalInMillis);
+ }
+ }
+
+ /**
+ * Notify the client of a video stream loss.
+ *
+ * @param callback A callback object we're about to stop forwarding frmae buffers and events.
+ */
+ private void notifyStreamStopped(ICarEvsStreamCallback callback) {
+ if (callback == null) {
+ return;
+ }
+
+ try {
+ int taggedEvent = CarEvsUtils.putTag(mServiceType,
+ CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
+ callback.onStreamEvent(taggedEvent);
+ } catch (RemoteException e) {
+ // Likely the binder death incident
+ Slogf.w(TAG_EVS, Log.getStackTraceString(e));
+ }
+ }
+
+ /**
+ * Check whether or not a given token is a valid session token that can be used to prioritize
+ * requests.
+ *
+ * @param token A IBinder object a caller wants to confirm.
+ *
+ * @return true if a given IBinder object is a valid session token.
+ * false otherwise.
+ */
+ @GuardedBy("mLock")
+ private boolean isSessionTokenLocked(IBinder token) {
+ return token != null && mService.isSessionToken(token);
+ }
+
+ /**
+ * Handle a transition from current state to UNAVAILABLE state.
+ *
+ * When the native EVS service becomes unavailable, CarEvsService notifies all active clients
+ * and enters UNAVAILABLE state.
+ *
+ * @return ERROR_NONE always.
+ */
+ @GuardedBy("mLock")
+ private @CarEvsError int handleTransitionToUnavailableLocked() {
+ // This transition happens only when CarEvsService loses the active connection to the
+ // Extended View System service.
+ switch (mState) {
+ case SERVICE_STATE_UNAVAILABLE:
+ // Nothing to do
+ break;
+
+ default:
+ // Stops any active video stream
+ stopService();
+ break;
+ }
+
+ mState = SERVICE_STATE_UNAVAILABLE;
+ return ERROR_NONE;
+ }
+
+ /**
+ * Handle a transition from current state to INACTIVE state.
+ *
+ * INACTIVE state means that CarEvsService is connected to the EVS service and idles.
+ *
+ * @return ERROR_BUSY if CarEvsService is already busy with a higher priority client.
+ * ERROR_NONE otherwise.
+ */
+ @GuardedBy("mLock")
+ private @CarEvsError int handleTransitionToInactiveLocked(int priority,
+ ICarEvsStreamCallback callback) {
+
+ switch (mState) {
+ case SERVICE_STATE_UNAVAILABLE:
+ if (callback != null) {
+ // We get a request to stop a video stream after losing a native EVS
+ // service. Simply unregister a callback and return.
+ if (!mHalCallback.unregister(callback)) {
+ Slogf.d(mLogTag, "Ignored a request to unregister unknown callback %s",
+ callback);
+ }
+ return ERROR_NONE;
+ } else {
+ // Requested to connect to the Extended View System service
+ if (!mHalWrapper.connectToHalServiceIfNecessary()) {
+ return ERROR_UNAVAILABLE;
+ }
+
+ if (needToStartActivityLocked()) {
+ // Request to launch the viewer because we lost the Extended View System
+ // service while a client was actively streaming a video.
+ mHandler.postDelayed(mActivityRequestTimeoutRunnable,
+ STREAM_START_REQUEST_TIMEOUT_MS);
+ }
+ }
+ break;
+
+ case SERVICE_STATE_INACTIVE:
+ // Nothing to do
+ break;
+
+ case SERVICE_STATE_REQUESTED:
+ // Requested to cancel a pending service request
+ if (priority < mLastRequestPriority) {
+ return ERROR_BUSY;
+ }
+
+ // Reset a timer for this new request
+ mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
+ break;
+
+ case SERVICE_STATE_ACTIVE:
+ // Remove pending callbacks and notify a client.
+ if (callback != null) {
+ mHandler.postAtFrontOfQueue(() -> notifyStreamStopped(callback));
+ if (!mHalCallback.unregister(callback)) {
+ Slogf.e(mLogTag, "Ignored a request to unregister unknown callback %s",
+ callback);
+ }
+
+ if (mPrivilegedCallback != null &&
+ callback.asBinder() == mPrivilegedCallback.asBinder()) {
+ mPrivilegedCallback = null;
+ invalidateSessionTokenLocked();
+ }
+ }
+
+ mHalWrapper.requestToStopVideoStream();
+ if (!mHalCallback.isEmpty()) {
+ Slogf.i(mLogTag, "%s streaming client(s) is/are alive.", mHalCallback.size());
+ return ERROR_NONE;
+ }
+
+ Slogf.i(mLogTag, "Last streaming client has been disconnected.");
+ mBufferRecords.clear();
+ break;
+
+ default:
+ throw new IllegalStateException("CarEvsService is in the unknown state.");
+ }
+
+ mState = SERVICE_STATE_INACTIVE;
+ return ERROR_NONE;
+ }
+
+ /**
+ * Handle a transition from current state to REQUESTED state.
+ *
+ * CarEvsService enters this state when it is requested to launch a registered camera activity.
+ *
+ * @return ERROR_UNAVAILABLE if CarEvsService is not connected to the native EVS service.
+ * ERROR_BUSY if CarEvsService is processing a higher priority client.
+ * ERROR_NONE otherwise.
+ */
+ @GuardedBy("mLock")
+ private @CarEvsError int handleTransitionToRequestedLocked(int priority) {
+ if (mActivityName == null) {
+ Slogf.e(mLogTag, "No activity is registered.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ switch (mState) {
+ case SERVICE_STATE_UNAVAILABLE:
+ // Attempts to connect to the native EVS service and transits to the
+ // REQUESTED state if it succeeds.
+ if (!mHalWrapper.connectToHalServiceIfNecessary()) {
+ return ERROR_UNAVAILABLE;
+ }
+ break;
+
+ case SERVICE_STATE_INACTIVE:
+ // Nothing to do
+ break;
+
+ case SERVICE_STATE_REQUESTED:
+ if (priority < mLastRequestPriority) {
+ // A current service request has a lower priority than a previous
+ // service request.
+ Slogf.e(TAG_EVS, "CarEvsService is busy with a higher priority client.");
+ return ERROR_BUSY;
+ }
+
+ // Reset a timer for this new request if it exists.
+ mHandler.removeCallbacks(mActivityRequestTimeoutRunnable);
+ break;
+
+ case SERVICE_STATE_ACTIVE:
+ if (priority < mLastRequestPriority) {
+ // We decline a request because CarEvsService is busy with a higher priority
+ // client.
+ Slogf.e(TAG_EVS, "CarEvsService is busy with a higher priority client.");
+ return ERROR_BUSY;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("CarEvsService is in the unknown state.");
+ }
+
+ mState = SERVICE_STATE_REQUESTED;
+ mLastRequestPriority = priority;
+
+ Intent evsIntent = new Intent(Intent.ACTION_MAIN)
+ .setComponent(mActivityName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+ .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ // Stores a token and arms the timer for the high-priority request.
+ Bundle bundle = new Bundle();
+ if (priority == REQUEST_PRIORITY_HIGH) {
+ bundle.putBinder(CarEvsManager.EXTRA_SESSION_TOKEN,
+ mService.generateSessionTokenInternal());
+ mHandler.postDelayed(
+ mActivityRequestTimeoutRunnable, STREAM_START_REQUEST_TIMEOUT_MS);
+ }
+ // Temporary, we use CarEvsManager.SERVICE_TYPE_REARVIEW as the key for a service type
+ // value.
+ bundle.putShort(Integer.toString(CarEvsManager.SERVICE_TYPE_REARVIEW),
+ (short) mServiceType);
+ evsIntent.replaceExtras(bundle);
+
+ mContext.startActivity(evsIntent);
+ return ERROR_NONE;
+ }
+
+ /**
+ * Handle a transition from current state to ACTIVE state.
+ *
+ * @return ERROR_BUSY if CarEvsService is busy with a higher priority client.
+ * ERROR_UNAVAILABLE if CarEvsService is in UNAVAILABLE state or fails to start a video
+ * stream.
+ * ERROR_NONE otherwise.
+ */
+ @GuardedBy("mLock")
+ private @CarEvsError int handleTransitionToActiveLocked(int priority, IBinder token,
+ ICarEvsStreamCallback callback) {
+
+ @CarEvsError int result = ERROR_NONE;
+ switch (mState) {
+ case SERVICE_STATE_UNAVAILABLE:
+ // We do not have a valid connection to the Extended View System service.
+ if (!mHalWrapper.connectToHalServiceIfNecessary()) {
+ return ERROR_UNAVAILABLE;
+ }
+ // fallthrough
+
+ case SERVICE_STATE_INACTIVE:
+ // CarEvsService receives a low priority request to start a video stream.
+ result = startService();
+ if (result != ERROR_NONE) {
+ return result;
+ }
+ break;
+
+ case SERVICE_STATE_REQUESTED:
+ // CarEvsService is reserved for higher priority clients.
+ if (priority == REQUEST_PRIORITY_HIGH && !isSessionTokenLocked(token)) {
+ // Declines a request with an expired token.
+ return ERROR_BUSY;
+ }
+
+ result = startService();
+ if (result != ERROR_NONE) {
+ return result;
+ }
+ break;
+
+ case SERVICE_STATE_ACTIVE:
+ // CarEvsManager will transfer an active video stream to a new client with a
+ // higher or equal priority.
+ if (priority < mLastRequestPriority) {
+ Slogf.i(mLogTag, "Declines a service request with a lower priority.");
+ break;
+ }
+
+ result = startService();
+ if (result != ERROR_NONE) {
+ return result;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("CarEvsService is in the unknown state.");
+ }
+
+ result = startVideoStream(callback, token);
+ if (result == ERROR_NONE) {
+ mState = SERVICE_STATE_ACTIVE;
+ mLastRequestPriority = priority;
+ if (isSessionTokenLocked(token)) {
+ mSessionToken = token;
+ mPrivilegedCallback = callback;
+ }
+ }
+ return result;
+ }
+
+ /** Connects to the native EVS service if necessary and opens a target camera device. */
+ private @CarEvsError int startService() {
+ if (!mHalWrapper.connectToHalServiceIfNecessary()) {
+ Slogf.e(mLogTag, "Failed to connect to EVS service.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ String cameraId = mCameraIdOverride != null ? mCameraIdOverride : mCameraId;
+ if (!mHalWrapper.openCamera(cameraId)) {
+ Slogf.e(mLogTag, "Failed to open a targer camera device, %s", cameraId);
+ return ERROR_UNAVAILABLE;
+ }
+
+ return ERROR_NONE;
+ }
+
+ /** Registers a callback and requests a video stream. */
+ private @CarEvsError int startVideoStream(ICarEvsStreamCallback callback, IBinder token) {
+ if (!mHalCallback.register(callback, token)) {
+ Slogf.e(mLogTag, "Failed to set a stream callback.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ if (!mHalWrapper.requestToStartVideoStream()) {
+ Slogf.e(mLogTag, "Failed to start a video stream.");
+ return ERROR_UNAVAILABLE;
+ }
+
+ return ERROR_NONE;
+ }
+
+ /** Waits for a video stream request from the System UI with a valid token. */
+ private void handleActivityRequestTimeout() {
+ // No client has responded to a state transition to the REQUESTED
+ // state before the timer expires. CarEvsService sends a
+ // notification again if it's still needed.
+ Slogf.d(mLogTag, "Timer expired. Request to launch the activity again.");
+ if (startActivityIfNecessary(/* resetState= */ true) != ERROR_NONE) {
+ Slogf.w(mLogTag, "Failed to request an activity.");
+ }
+ }
+
+ /** Invalidates current session token. */
+ @GuardedBy("mLock")
+ private void invalidateSessionTokenLocked() {
+ mService.invalidateSessionToken(mSessionToken);
+ mSessionToken = null;
+ }
+
+ /** Checks whether or not we need to request a registered camera activity. */
+ @GuardedBy("mLock")
+ private boolean checkCurrentStateRequiresSystemActivityLocked() {
+ return (mState == SERVICE_STATE_ACTIVE || mState == SERVICE_STATE_REQUESTED) &&
+ mLastRequestPriority == REQUEST_PRIORITY_HIGH;
+ }
+
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+ private String stateToString(@CarEvsServiceState int state) {
+ switch (state) {
+ case SERVICE_STATE_UNAVAILABLE:
+ return "UNAVAILABLE";
+ case SERVICE_STATE_INACTIVE:
+ return "INACTIVE";
+ case SERVICE_STATE_REQUESTED:
+ return "REQUESTED";
+ case SERVICE_STATE_ACTIVE:
+ return "ACTIVE";
+ default:
+ return "UNKNOWN: " + state;
+ }
+ }
+
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+ @Override
+ public String toString() {
+ synchronized (mLock) {
+ return stateToString(mState);
+ }
+ }
+
+ /** Overrides a current state. */
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE)
+ @VisibleForTesting
+ void setState(@CarEvsServiceState int newState) {
+ synchronized (mLock) {
+ Slogf.d(mLogTag, "StateMachine(%s)'s state has been changed from %s to %s.",
+ this, mState, newState);
+ mState = newState;
+ }
+ }
+
+ /** Overrides a current callback object. */
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE)
+ @VisibleForTesting
+ void addStreamCallback(ICarEvsStreamCallback callback) {
+ Slogf.d(mLogTag, "Register additional callback %s", callback);
+ mHalCallback.register(callback, /* token= */ null);
+ }
+
+ /** Overrides a current valid session token. */
+ @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+ @VisibleForTesting
+ void setSessionToken(IBinder token) {
+ synchronized (mLock) {
+ Slogf.d(mLogTag, "SessionToken %s is replaced with %s", mSessionToken, token);
+ mSessionToken = token;
+ }
+ }
+}
diff --git a/service/src/com/android/car/hal/HalPropValue.java b/service/src/com/android/car/hal/HalPropValue.java
index a7047f3..6fe8d06 100644
--- a/service/src/com/android/car/hal/HalPropValue.java
+++ b/service/src/com/android/car/hal/HalPropValue.java
@@ -22,6 +22,8 @@
import android.hardware.automotive.vehicle.VehiclePropertyType;
import android.util.Log;
+import com.android.car.internal.property.CarPropertyHelper;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -168,20 +170,12 @@
/**
* Turns this class to a {@link CarPropertyValue}.
*
- * The property status must be {@link VehiclePropertyStatus#AVAILABLE}.
- *
* @param mgrPropId The property ID used in {@link android.car.VehiclePropertyIds}.
* @param config The config for the property.
* @return A CarPropertyValue that could be passed to upper layer
- * @throws IllegalStateException If the property value is not valid or the property status
- * is not available.
+ * @throws IllegalStateException If property has unsupported type
*/
public CarPropertyValue toCarPropertyValue(int mgrPropId, HalPropConfig config) {
- if (getStatus() != VehiclePropertyStatus.AVAILABLE) {
- throw new IllegalStateException("The property status is " + getStatus()
- + ", not available, cannot convert to CarPropertyValue. This should never"
- + " happen and is likely indicating a bug.");
- }
if (isMixedTypeProperty(getPropId())) {
int[] configArray = config.getConfigArray();
boolean containStringType = configArray[0] == 1;
@@ -270,74 +264,74 @@
private CarPropertyValue<?> toCarPropertyValue(int propertyId) {
Class<?> clazz = CarPropertyUtils.getJavaClass(getPropId() & VehiclePropertyType.MASK);
int areaId = getAreaId();
- int status = getStatus();
+ int status = vehiclePropertyStatusToCarPropertyStatus(getStatus());
long timestampNanos = getTimestamp();
- String propertyName = VehiclePropertyIds.toString(propertyId);
+ Object value = null;
- // Handles each return value from {@link getJavaClass}.
if (Boolean.class == clazz) {
- if (getInt32ValuesSize() < 1) {
- throw new IllegalStateException("empty int32 values for property ID: "
- + propertyName + ", areaID: " + areaId);
+ if (getInt32ValuesSize() > 0) {
+ value = Boolean.valueOf(getInt32Value(0) == 1);
+ } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
+ status = CarPropertyValue.STATUS_ERROR;
}
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos,
- getInt32Value(0) == 1);
} else if (Float.class == clazz) {
- if (getFloatValuesSize() < 1) {
- throw new IllegalStateException("empty float values for property ID: "
- + propertyName + ", areaID: " + areaId);
+ if (getFloatValuesSize() > 0) {
+ value = Float.valueOf(getFloatValue(0));
+ } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
+ status = CarPropertyValue.STATUS_ERROR;
}
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, getFloatValue(0));
} else if (Integer.class == clazz) {
- if (getInt32ValuesSize() < 1) {
- throw new IllegalStateException("empty int32 values for property ID: "
- + propertyName + ", areaID: " + areaId);
+ if (getInt32ValuesSize() > 0) {
+ value = Integer.valueOf(getInt32Value(0));
+ } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
+ status = CarPropertyValue.STATUS_ERROR;
}
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, getInt32Value(0));
} else if (Long.class == clazz) {
- if (getInt64ValuesSize() < 1) {
- throw new IllegalStateException("empty int64 values for property ID: "
- + propertyName + ", areaID: " + areaId);
+ if (getInt64ValuesSize() > 0) {
+ value = Long.valueOf(getInt64Value(0));
+ } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
+ status = CarPropertyValue.STATUS_ERROR;
}
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, getInt64Value(0));
} else if (Float[].class == clazz) {
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos,
- getFloatContainerArray());
+ value = getFloatContainerArray();
} else if (Integer[].class == clazz) {
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos,
- getInt32ContainerArray());
+ value = getInt32ContainerArray();
} else if (Long[].class == clazz) {
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos,
- getInt64ContainerArray());
+ value = getInt64ContainerArray();
} else if (String.class == clazz) {
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, getStringValue());
+ value = getStringValue();
} else if (byte[].class == clazz) {
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, getByteArray());
+ value = getByteArray();
} else {
- throw new IllegalStateException("Unexpected type in: " + propertyName);
+ throw new IllegalStateException(
+ "Unexpected type " + clazz + " - propertyId: " + VehiclePropertyIds.toString(
+ propertyId));
}
+ if (value == null) {
+ value = CarPropertyHelper.getDefaultValue(clazz);
+ }
+ return new CarPropertyValue<>(propertyId, areaId, status, timestampNanos, value);
}
private CarPropertyValue<?> toMixedCarPropertyValue(
int propertyId, boolean containBoolean, boolean containString) {
int areaId = getAreaId();
- int status = getStatus();
+ int status = vehiclePropertyStatusToCarPropertyStatus(getStatus());
long timestampNanos = getTimestamp();
- String propertyName = VehiclePropertyIds.toString(propertyId);
List<Object> valuesList = new ArrayList<>();
if (containString) {
valuesList.add(getStringValue());
}
if (containBoolean) {
- if (getInt32ValuesSize() < 1) {
- throw new IllegalStateException("empty int32 values for property ID: "
- + propertyName + ", areaID: " + areaId);
- }
- boolean boolValue = getInt32Value(0) == 1;
- valuesList.add(boolValue);
- for (int i = 1; i < getInt32ValuesSize(); i++) {
- valuesList.add(getInt32Value(i));
+ if (getInt32ValuesSize() > 0) {
+ boolean boolValue = getInt32Value(0) == 1;
+ valuesList.add(boolValue);
+ for (int i = 1; i < getInt32ValuesSize(); i++) {
+ valuesList.add(getInt32Value(i));
+ }
+ } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
+ status = CarPropertyValue.STATUS_ERROR;
}
} else {
for (int i = 0; i < getInt32ValuesSize(); i++) {
@@ -353,7 +347,8 @@
for (int i = 0; i < getByteValuesSize(); i++) {
valuesList.add(getByteValue(i));
}
- return new CarPropertyValue<>(propertyId, areaId, timestampNanos, valuesList.toArray());
+ return new CarPropertyValue<>(propertyId, areaId, status, timestampNanos,
+ valuesList.toArray());
}
private boolean equalInt32Values(HalPropValue argument) {
@@ -391,4 +386,17 @@
}
return true;
}
+
+ private static @CarPropertyValue.PropertyStatus int vehiclePropertyStatusToCarPropertyStatus(
+ @VehiclePropertyStatus int status) {
+ switch (status) {
+ case VehiclePropertyStatus.AVAILABLE:
+ return CarPropertyValue.STATUS_AVAILABLE;
+ case VehiclePropertyStatus.ERROR:
+ return CarPropertyValue.STATUS_ERROR;
+ case VehiclePropertyStatus.UNAVAILABLE:
+ return CarPropertyValue.STATUS_UNAVAILABLE;
+ }
+ return CarPropertyValue.STATUS_ERROR;
+ }
}
diff --git a/service/src/com/android/car/hal/PropertyHalService.java b/service/src/com/android/car/hal/PropertyHalService.java
index 19aa3df..589c22c 100644
--- a/service/src/com/android/car/hal/PropertyHalService.java
+++ b/service/src/com/android/car/hal/PropertyHalService.java
@@ -751,20 +751,6 @@
return MGR_PROP_ID_TO_HAL_PROP_ID.getKey(halPropId, halPropId);
}
- private static void checkHalPropValueStatus(HalPropValue halPropValue, int mgrPropId,
- int areaId) {
- if (halPropValue.getStatus() == VehiclePropertyStatus.UNAVAILABLE) {
- throw new ServiceSpecificException(STATUS_NOT_AVAILABLE,
- "VHAL returned property status as UNAVAILABLE for property: "
- + VehiclePropertyIds.toString(mgrPropId) + ", areaId: " + areaId);
- }
- if (halPropValue.getStatus() == VehiclePropertyStatus.ERROR) {
- throw new ServiceSpecificException(STATUS_INTERNAL_ERROR,
- "VHAL returned property status as ERROR for property: "
- + VehiclePropertyIds.toString(mgrPropId) + ", areaId: " + areaId);
- }
- }
-
/**
* Maybe finish the pending set value request depending on the updated value.
*
@@ -980,7 +966,7 @@
synchronized (mLock) {
halPropConfig = mHalPropIdToPropConfig.get(halPropId);
}
- checkHalPropValueStatus(halPropValue, mgrPropId, areaId);
+ halPropValue = mVehicleHal.get(halPropId, areaId);
try {
return halPropValue.toCarPropertyValue(mgrPropId, halPropConfig);
} catch (IllegalStateException e) {
@@ -1162,19 +1148,25 @@
if (DBG) {
Slogf.d(TAG, "takeSupportedProperties: %s", halPropIdToName(halPropId));
}
+ } else {
+ if (DBG) {
+ Slogf.d(TAG, "takeProperties: Property: %s is not supported, ignore",
+ halPropIdToName(halPropId));
+ }
}
}
if (DBG) {
Slogf.d(TAG, "takeSupportedProperties() took %d properties", halPropConfigs.size());
}
// If vehicle hal support to select permission for vendor properties.
- HalPropConfig customizePermission;
- synchronized (mLock) {
- customizePermission = mHalPropIdToPropConfig.get(
- VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION);
- }
+ HalPropConfig customizePermission = mVehicleHal.getPropConfig(
+ VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION);
if (customizePermission != null) {
mPropertyHalServiceIds.customizeVendorPermission(customizePermission.getConfigArray());
+ } else {
+ if (DBG) {
+ Slogf.d(TAG, "No custom vendor permission defined in VHAL");
+ }
}
}
@@ -1320,9 +1312,9 @@
continue;
}
int mgrPropId = halToManagerPropId(halPropId);
- if (halPropValue.getStatus() != VehiclePropertyStatus.AVAILABLE) {
- Slogf.w(TAG, "Drop event %s with status that is not AVAILABLE", halPropValue);
- continue;
+ if (DBG && halPropValue.getStatus() != VehiclePropertyStatus.AVAILABLE) {
+ Slogf.d(TAG, "Received event %s with status that is not AVAILABLE",
+ halPropValue);
}
try {
CarPropertyValue<?> carPropertyValue = halPropValue.toCarPropertyValue(
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
index fd0b902..193d8d6 100644
--- a/service/src/com/android/car/hal/PropertyHalServiceIds.java
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -59,6 +59,7 @@
import android.hardware.automotive.vehicle.VehicleOilLevel;
import android.hardware.automotive.vehicle.VehicleProperty;
import android.hardware.automotive.vehicle.VehiclePropertyGroup;
+import android.hardware.automotive.vehicle.VehiclePropertyStatus;
import android.hardware.automotive.vehicle.VehiclePropertyType;
import android.hardware.automotive.vehicle.VehicleSeatOccupancyState;
import android.hardware.automotive.vehicle.VehicleTurnSignal;
@@ -900,9 +901,6 @@
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
mHalPropIdsForUnits.add(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
- mHalPropIdToPermissions.put(VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, new Pair<>(
- Car.PERMISSION_READ_CAR_VENDOR_PERMISSION_INFO,
- null));
mHalPropIdToPermissions.put(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE,
new Pair<>(Car.PERMISSION_CAR_INFO, null));
mHalPropIdToPermissions.put(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS,
@@ -1128,6 +1126,9 @@
if ((propId & VehiclePropertyType.MASK) == VehiclePropertyType.MIXED) {
return true;
}
+ if (propValue.getStatus() != VehiclePropertyStatus.AVAILABLE) {
+ return true;
+ }
if (!checkFormatForAllProperties(propValue)) {
Slogf.e(TAG, "Property value" + propValue + "has an invalid data format");
return false;
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index 322b50f..1dda6b9 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -974,6 +974,15 @@
}
/**
+ * Gets the property config for a property, returns {@code null} if not supported.
+ */
+ public @Nullable HalPropConfig getPropConfig(int propId) {
+ synchronized (mLock) {
+ return mAllProperties.get(propId);
+ }
+ }
+
+ /**
* Checks whether we are connected to AIDL VHAL: {@code true} or HIDL VHAL: {@code false}.
*/
public boolean isAidlVhal() {
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index ed32764..7f45e58 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -1630,7 +1630,8 @@
int displayId, String blockedActivity, int blockedTaskId, String taskRootActivity,
boolean isRootDo) {
Intent newActivityIntent = new Intent();
- newActivityIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ newActivityIntent.setFlags(
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
newActivityIntent.setComponent(blockingActivity);
newActivityIntent.putExtra(
BLOCKING_INTENT_EXTRA_DISPLAY_ID, displayId);
diff --git a/service/src/com/android/car/pm/VendorServiceController.java b/service/src/com/android/car/pm/VendorServiceController.java
index 4e54b73..b40a91f 100644
--- a/service/src/com/android/car/pm/VendorServiceController.java
+++ b/service/src/com/android/car/pm/VendorServiceController.java
@@ -490,9 +490,8 @@
*/
@VisibleForTesting
public static final class VendorServiceConnection implements ServiceConnection, Executor {
- private static final int REBIND_DELAY_MS = 5000;
- private static final int MAX_RECENT_FAILURES = 5;
- private static final int FAILURE_COUNTER_RESET_TIMEOUT = 5 * 60 * 1000; // 5 min.
+ private static final int INITIAL_REBIND_DELAY_MS = 4000; // 4 sec.
+ private static final int DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT = 5 * 60 * 1000; // 5 min.
private static final int MSG_REBIND = 0;
private static final int MSG_FAILURE_COUNTER_RESET = 1;
@@ -627,8 +626,13 @@
int currentUserId = ActivityManager.getCurrentUser();
if (isUserInScope(mUser.getIdentifier(), mVendorServiceInfo, mCarUserService,
currentUserId)) {
+ // Double the delay after each failure.
+ int rebindDelay = INITIAL_REBIND_DELAY_MS * (1 << mRecentFailures);
+ Slogf.i(TAG, "tryToRebind(): after " + mRecentFailures + " recent failures,"
+ + " trying to rebind service " + mVendorServiceInfo.toShortString()
+ + " for user " + mUser.getIdentifier() + " in " + rebindDelay + "ms");
mFailureHandler.sendMessageDelayed(
- mFailureHandler.obtainMessage(MSG_REBIND), REBIND_DELAY_MS);
+ mFailureHandler.obtainMessage(MSG_REBIND), rebindDelay);
scheduleResetFailureCounter();
} else {
Slogf.w(TAG, "No need to rebind anymore as the service no longer satisfies "
@@ -638,22 +642,34 @@
private void scheduleResetFailureCounter() {
mFailureHandler.removeMessages(MSG_FAILURE_COUNTER_RESET);
+ // Reset the failure counter after the timeout. We take the max, to ensure
+ // that we are not resetting the counter before exhausting all retries.
+ int failureCounterResetTimeout =
+ INITIAL_REBIND_DELAY_MS * (1 << (mVendorServiceInfo.getMaxRetries() + 1));
+ failureCounterResetTimeout =
+ failureCounterResetTimeout > DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT
+ ? failureCounterResetTimeout : DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT;
mFailureHandler.sendMessageDelayed(
mFailureHandler.obtainMessage(MSG_FAILURE_COUNTER_RESET),
- FAILURE_COUNTER_RESET_TIMEOUT);
+ failureCounterResetTimeout);
}
private void handleFailureMessage(Message msg) {
switch (msg.what) {
case MSG_REBIND: {
- if (mRecentFailures < MAX_RECENT_FAILURES && !mBound) {
+ if (mBound) {
+ Slogf.d(TAG, "Service " + mVendorServiceInfo.toShortString()
+ + " is already bound. Ignoring MSG_REBIND");
+ } else if (mRecentFailures < mVendorServiceInfo.getMaxRetries()) {
Slogf.i(TAG, "Attempting to rebind to the service "
- + mVendorServiceInfo.toShortString());
+ + mVendorServiceInfo.toShortString() + " (" + (mRecentFailures + 1)
+ + " out of " + mVendorServiceInfo.getMaxRetries() + " max tries)");
++mRecentFailures;
startOrBindService();
} else {
- Slogf.w(TAG, "Exceeded maximum number of attempts to rebind"
- + "to the service " + mVendorServiceInfo.toShortString());
+ Slogf.w(TAG, "Exceeded maximum number of attempts ("
+ + mVendorServiceInfo.getMaxRetries() + ") to rebind to the service "
+ + mVendorServiceInfo.toShortString());
}
break;
}
diff --git a/service/src/com/android/car/pm/VendorServiceInfo.java b/service/src/com/android/car/pm/VendorServiceInfo.java
index 079498b..f075ee6 100644
--- a/service/src/com/android/car/pm/VendorServiceInfo.java
+++ b/service/src/com/android/car/pm/VendorServiceInfo.java
@@ -22,6 +22,8 @@
import android.content.Intent;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -30,9 +32,14 @@
* immutable object to store only service configuration.
*/
final class VendorServiceInfo {
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_RETRIES = 6;
+
private static final String KEY_BIND = "bind";
private static final String KEY_USER_SCOPE = "user";
private static final String KEY_TRIGGER = "trigger";
+ private static final String KEY_MAX_RETRIES = "maxRetries";
private static final int USER_SCOPE_ALL = 0;
private static final int USER_SCOPE_SYSTEM = 1;
@@ -76,15 +83,17 @@
private final @Bind int mBind;
private final @UserScope int mUserScope;
private final @Trigger int mTrigger;
+ private final int mMaxRetries;
@Nullable
private final ComponentName mComponentName;
private VendorServiceInfo(@Nullable ComponentName componentName, @Bind int bind,
- @UserScope int userScope, @Trigger int trigger) {
+ @UserScope int userScope, @Trigger int trigger, int maxRetries) {
mComponentName = componentName;
mUserScope = userScope;
mTrigger = trigger;
mBind = bind;
+ mMaxRetries = maxRetries;
}
boolean isAllUserService() {
@@ -131,6 +140,10 @@
return mBind == START_FOREGROUND;
}
+ int getMaxRetries() {
+ return mMaxRetries;
+ }
+
Intent getIntent() {
Intent intent = new Intent();
intent.setComponent(mComponentName);
@@ -154,6 +167,7 @@
int bind = START;
int userScope = USER_SCOPE_ALL;
int trigger = TRIGGER_UNLOCKED;
+ int maxRetries = DEFAULT_MAX_RETRIES;
if (serviceParamTokens.length == 2) {
for (String keyValueStr : serviceParamTokens[1].split(",")) {
@@ -220,13 +234,22 @@
throw new IllegalArgumentException("Unexpected trigger: " + val);
}
break;
+ case KEY_MAX_RETRIES:
+ try {
+ maxRetries = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ "Cannot parse the specified `maxRetries` into an integer: "
+ + val);
+ }
+ break;
default:
throw new IllegalArgumentException("Unexpected token: " + key);
}
}
}
- return new VendorServiceInfo(cn, bind, userScope, trigger);
+ return new VendorServiceInfo(cn, bind, userScope, trigger, maxRetries);
}
@Override
@@ -236,6 +259,7 @@
+ ", bind=" + bindToString(mBind)
+ ", trigger=" + triggerToString(mTrigger)
+ ", userScope=" + userScopeToString(mUserScope)
+ + (mMaxRetries == DEFAULT_MAX_RETRIES ? "" : ", maxRetries=" + mMaxRetries)
+ '}';
}
diff --git a/service/src/com/android/car/power/CarPowerManagementService.java b/service/src/com/android/car/power/CarPowerManagementService.java
index 33b2649..b70df63 100644
--- a/service/src/com/android/car/power/CarPowerManagementService.java
+++ b/service/src/com/android/car/power/CarPowerManagementService.java
@@ -243,7 +243,7 @@
@GuardedBy("mLock")
private boolean mShutdownOnNextSuspend;
@GuardedBy("mLock")
- private boolean mIsBooting = true;
+ private boolean mShouldResumeUserService;
@GuardedBy("mLock")
private int mShutdownPrepareTimeMs = MIN_MAX_GARAGE_MODE_DURATION_MS;
@GuardedBy("mLock")
@@ -495,12 +495,14 @@
void setStateForWakeUp() {
mSilentModeHandler.init();
synchronized (mLock) {
- mIsBooting = false;
+ mShouldResumeUserService = true;
}
handleWaitForVhal(new CpmsState(CpmsState.WAIT_FOR_VHAL,
CarPowerManager.STATE_WAIT_FOR_VHAL, /* canPostpone= */ false));
- Slogf.d(TAG, "setStateForTesting(): mIsBooting is set to false and power state is switched "
- + "to Wait For Vhal");
+ Slogf.d(TAG,
+ "setStateForTesting(): mShouldResumeUserService is set to false and power state "
+ + "is switched "
+ + "to Wait For Vhal");
}
/**
@@ -713,10 +715,11 @@
mHal.sendOn();
synchronized (mLock) {
- if (mIsBooting) {
+ if (!mShouldResumeUserService) {
Slogf.d(TAG, "handleOn(): called on boot");
- mIsBooting = false;
return;
+ } else {
+ mShouldResumeUserService = false;
}
}
@@ -1135,6 +1138,9 @@
// allowUserSwitch value doesn't matter for onSuspend = true
mUserService.onSuspend();
+ synchronized (mLock) {
+ mShouldResumeUserService = true;
+ }
}
private void waitForCompletion(Runnable taskAtCompletion, Runnable taskAtInterval,
@@ -1661,12 +1667,18 @@
@Override
public void setDisplayPowerState(int displayId, boolean enable) {
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
+ boolean isNotSelf = Binder.getCallingUid() != Process.myUid();
CarOccupantZoneService occupantZoneService =
CarLocalServices.getService(CarOccupantZoneService.class);
- if (displayId == occupantZoneService.getDisplayIdForDriver(
- CarOccupantZoneManager.DISPLAY_TYPE_MAIN)
- && Binder.getCallingUid() != Process.myUid()) {
- throw new UnsupportedOperationException("Driver display control is not supported");
+ long token = Binder.clearCallingIdentity();
+ try {
+ int driverDisplayId = occupantZoneService.getDisplayIdForDriver(
+ CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
+ if (displayId == driverDisplayId && isNotSelf) {
+ throw new UnsupportedOperationException("Driver display control is not supported");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
mSystemInterface.setDisplayState(displayId, enable);
}
diff --git a/service/src/com/android/car/power/PolicyReader.java b/service/src/com/android/car/power/PolicyReader.java
index f30a47d..17c597c 100644
--- a/service/src/com/android/car/power/PolicyReader.java
+++ b/service/src/com/android/car/power/PolicyReader.java
@@ -393,9 +393,12 @@
throw new PolicyXmlException("invalid XML version: " + version);
}
- ArrayMap<String, CarPowerPolicy> registeredPolicies = new ArrayMap<>();
+ ArrayMap<String, CarPowerPolicy> registeredPolicies;
+ List<IntermediateCarPowerPolicy> intermediateCarPowerPolicies = new ArrayList<>();
ArrayMap<String, SparseArray<String>> policyGroups = new ArrayMap<>();
- CarPowerPolicy systemPolicyOverride = null;
+ List<IntermediateCarPowerPolicy> intermediateSystemPolicyOverride = new ArrayList<>();
+ ArrayMap<String, Integer> customComponents = new ArrayMap<>();
+ CarPowerPolicy systemPolicyOverride;
String defaultGroupPolicyId = null;
int type;
@@ -403,7 +406,7 @@
if (type != START_TAG) continue;
switch (parser.getName()) {
case TAG_POLICIES:
- registeredPolicies = parsePolicies(parser, true);
+ intermediateCarPowerPolicies.addAll(parsePolicies(parser, true));
break;
case TAG_POLICY_GROUPS:
defaultGroupPolicyId = parser.getAttributeValue(NAMESPACE,
@@ -411,19 +414,22 @@
policyGroups = parsePolicyGroups(parser);
break;
case TAG_SYSTEM_POLICY_OVERRIDES:
- systemPolicyOverride = parseSystemPolicyOverrides(parser);
+ intermediateSystemPolicyOverride.addAll(parseSystemPolicyOverrides(parser));
break;
case TAG_CUSTOM_COMPONENTS:
- mCustomComponents = parseCustomComponents(parser);
+ customComponents = parseCustomComponents(parser);
break;
default:
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POWER_POLICY);
}
}
- // TODO(b/273315694) add validation of custom components
+ registeredPolicies = validatePowerPolicies(intermediateCarPowerPolicies, customComponents);
+ systemPolicyOverride = validateSystemOverrides(intermediateSystemPolicyOverride,
+ customComponents);
validatePolicyGroups(policyGroups, registeredPolicies, defaultGroupPolicyId);
+ mCustomComponents = customComponents;
mDefaultPolicyGroupId = defaultGroupPolicyId;
mRegisteredPowerPolicies = registeredPolicies;
registerBasicPowerPolicies();
@@ -456,10 +462,10 @@
return customComponentsMap;
}
- private ArrayMap<String, CarPowerPolicy> parsePolicies(XmlPullParser parser,
- boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException,
- IOException {
- ArrayMap<String, CarPowerPolicy> policies = new ArrayMap<>();
+ private List<IntermediateCarPowerPolicy> parsePolicies(XmlPullParser parser,
+ boolean includeOtherComponents)
+ throws PolicyXmlException, XmlPullParserException, IOException {
+ List<IntermediateCarPowerPolicy> policies = new ArrayList<>();
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
@@ -473,7 +479,7 @@
throw new PolicyXmlException("Policy ID should not start with "
+ SYSTEM_POWER_POLICY_PREFIX);
}
- policies.put(policyId, parsePolicy(parser, policyId, includeOtherComponents));
+ policies.add(parsePolicy(parser, policyId, includeOtherComponents));
} else {
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POLICIES);
@@ -503,11 +509,15 @@
return policyGroups;
}
- @Nullable
- private CarPowerPolicy parseSystemPolicyOverrides(XmlPullParser parser) throws
+ private List<IntermediateCarPowerPolicy> parseSystemPolicyOverrides(XmlPullParser parser) throws
PolicyXmlException, XmlPullParserException, IOException {
- ArrayMap<String, CarPowerPolicy> systemOverrides = parsePolicies(parser, false);
- int numOverrides = systemOverrides.size();
+ return parsePolicies(/* parser= */ parser, /* includeOtherComponents= */ false);
+ }
+ @Nullable
+ private CarPowerPolicy validateSystemOverrides(
+ List<IntermediateCarPowerPolicy> systemPolicyOverrideIntermediate,
+ ArrayMap<String, Integer> customComponents) throws PolicyXmlException {
+ int numOverrides = systemPolicyOverrideIntermediate.size();
if (numOverrides == 0) {
return null;
}
@@ -515,43 +525,32 @@
throw new PolicyXmlException("only one system power policy is supported: "
+ numOverrides + " system policies exist");
}
- CarPowerPolicy policyOverride =
- systemOverrides.get(POWER_POLICY_ID_NO_USER_INTERACTION);
- if (policyOverride == null) {
+ if (!systemPolicyOverrideIntermediate.get(0).policyId.equals(
+ POWER_POLICY_ID_NO_USER_INTERACTION)) {
throw new PolicyXmlException("system power policy id should be "
+ POWER_POLICY_ID_NO_USER_INTERACTION);
}
+
+ CarPowerPolicy policyOverride =
+ toCarPowerPolicy(systemPolicyOverrideIntermediate.get(0), customComponents);
+
Set<Integer> visited = new ArraySet<>();
checkSystemPowerPolicyComponents(policyOverride.getEnabledComponents(), visited);
checkSystemPowerPolicyComponents(policyOverride.getDisabledComponents(), visited);
return policyOverride;
}
- private CarPowerPolicy parsePolicy(XmlPullParser parser, String policyId,
- boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException,
- IOException {
- SparseBooleanArray components = new SparseBooleanArray();
+ private IntermediateCarPowerPolicy parsePolicy(XmlPullParser parser, String policyId,
+ boolean includeOtherComponents)
+ throws PolicyXmlException, IOException, XmlPullParserException {
+ ArrayMap<String, Boolean> components = new ArrayMap<>();
String behavior = POWER_ONOFF_UNTOUCHED;
boolean otherComponentsProcessed = false;
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
if (TAG_COMPONENT.equals(parser.getName())) {
- String id = parser.getAttributeValue(NAMESPACE, ATTR_ID);
- int powerComponent = toPowerComponent(id, true);
- if (powerComponent == INVALID_POWER_COMPONENT) {
- powerComponent = toCustomPowerComponentId(id);
- }
- if (powerComponent == INVALID_POWER_COMPONENT) {
- throw new PolicyXmlException(" Unknown component id : " + id);
- }
-
- if (components.indexOfKey(powerComponent) >= 0) {
- throw new PolicyXmlException(
- "invalid value(" + id + ") in |" + ATTR_ID + "| attribute of |"
- + TAG_COMPONENT
- + "| tag");
- }
+ String powerComponent = parser.getAttributeValue(NAMESPACE, ATTR_ID);
String state = getText(parser);
switch (state) {
case POWER_ONOFF_ON:
@@ -561,8 +560,9 @@
components.put(powerComponent, false);
break;
default:
- throw new PolicyXmlException("target state(" + state + ") for " + id
- + " is not valid");
+ throw new PolicyXmlException(
+ "target state(" + state + ") for " + powerComponent
+ + " is not valid");
}
skip(parser);
} else if (TAG_OTHER_COMPONENTS.equals(parser.getName())) {
@@ -596,11 +596,42 @@
+ TAG_POLICY);
}
}
+ return new IntermediateCarPowerPolicy(policyId, components, behavior);
+ }
+
+ private CarPowerPolicy toCarPowerPolicy(IntermediateCarPowerPolicy intermediatePolicy,
+ ArrayMap<String, Integer> customComponents)
+ throws PolicyXmlException {
+ SparseBooleanArray components = new SparseBooleanArray();
+
+ // Convert string values of IntermediateCarPowerPolicy to a CarPowerPolicy
+ ArrayMap<String, Boolean> intermediatePolicyComponents = intermediatePolicy.components;
+ for (int i = 0; i < intermediatePolicyComponents.size(); i++) {
+ String componentId = intermediatePolicyComponents.keyAt(i);
+
+ int powerComponent = toPowerComponent(componentId, true);
+ if (powerComponent == INVALID_POWER_COMPONENT) {
+ powerComponent = toCustomPowerComponentId(componentId, customComponents);
+ }
+ if (powerComponent == INVALID_POWER_COMPONENT) {
+ throw new PolicyXmlException(" Unknown component id : " + componentId);
+ }
+
+ if (components.indexOfKey(powerComponent) >= 0) {
+ throw new PolicyXmlException(
+ "invalid value(" + componentId + ") in |" + ATTR_ID + "| attribute of |"
+ + TAG_COMPONENT
+ + "| tag");
+ }
+ components.put(powerComponent, intermediatePolicyComponents.valueAt(i));
+ }
+
boolean enabled;
boolean untouched = false;
- if (POWER_ONOFF_ON.equals(behavior)) {
+
+ if (POWER_ONOFF_ON.equals(intermediatePolicy.otherBehavior)) {
enabled = true;
- } else if (POWER_ONOFF_OFF.equals(behavior)) {
+ } else if (POWER_ONOFF_OFF.equals(intermediatePolicy.otherBehavior)) {
enabled = false;
} else {
enabled = false;
@@ -612,21 +643,19 @@
if (components.indexOfKey(component) >= 0) continue;
components.put(component, enabled);
}
- for (int i = 0; i < mCustomComponents.size(); ++i) {
- int componentId = mCustomComponents.valueAt(i);
+ for (int i = 0; i < customComponents.size(); ++i) {
+ int componentId = customComponents.valueAt(i);
if (components.indexOfKey(componentId) < 0) { // key not found
components.put(componentId, enabled);
}
}
}
- return new CarPowerPolicy(policyId, toIntArray(components, true),
+ return new CarPowerPolicy(intermediatePolicy.policyId, toIntArray(components, true),
toIntArray(components, false));
}
- // this implementation rely on custom power policies to be parsed before policies
- // TODO(b/273315697) - rewrite to make position independent
- private int toCustomPowerComponentId(String id) {
- return mCustomComponents.getOrDefault(id, INVALID_POWER_COMPONENT);
+ private int toCustomPowerComponentId(String id, ArrayMap<String, Integer> customComponents) {
+ return customComponents.getOrDefault(id, INVALID_POWER_COMPONENT);
}
private SparseArray<String> parsePolicyGroup(XmlPullParser parser) throws PolicyXmlException,
@@ -676,6 +705,19 @@
return policyGroup;
}
+ private ArrayMap<String, CarPowerPolicy> validatePowerPolicies(
+ List<IntermediateCarPowerPolicy> intermediateCarPowerPolicies,
+ ArrayMap<String, Integer> customComponents) throws PolicyXmlException {
+ ArrayMap<String, CarPowerPolicy> powerPolicies = new ArrayMap<>();
+ for (int index = 0; index < intermediateCarPowerPolicies.size(); ++index) {
+ IntermediateCarPowerPolicy intermediateCarPowerPolicy =
+ intermediateCarPowerPolicies.get(index);
+ powerPolicies.put(intermediateCarPowerPolicy.policyId,
+ toCarPowerPolicy(intermediateCarPowerPolicy, customComponents));
+ }
+ return powerPolicies;
+ }
+
private void validatePolicyGroups(ArrayMap<String, SparseArray<String>> policyGroups,
ArrayMap<String, CarPowerPolicy> registeredPolicies, String defaultGroupPolicyId)
throws PolicyXmlException {
@@ -913,4 +955,17 @@
super(message);
}
}
+
+ private static final class IntermediateCarPowerPolicy {
+ public final String policyId;
+ public final ArrayMap<String, Boolean> components;
+ public final String otherBehavior;
+
+ IntermediateCarPowerPolicy(String policyId, ArrayMap<String, Boolean> components,
+ String behavior) {
+ this.policyId = policyId;
+ this.components = components;
+ this.otherBehavior = behavior;
+ }
+ }
}
diff --git a/service/src/com/android/car/remoteaccess/CarRemoteAccessService.java b/service/src/com/android/car/remoteaccess/CarRemoteAccessService.java
index d763c4b..92462ac 100644
--- a/service/src/com/android/car/remoteaccess/CarRemoteAccessService.java
+++ b/service/src/com/android/car/remoteaccess/CarRemoteAccessService.java
@@ -413,7 +413,6 @@
mNextPowerState = getLastShutdownState();
mIsReadyForRemoteTask = true;
mIsWakeupRequired = false;
- mNotifyApPowerStateRetryCount += 1;
}
mPowerService.registerListenerWithCompletion(mCarPowerStateListener);
@@ -539,8 +538,10 @@
CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_REMOTE_ACCESS);
Preconditions.checkArgument(callback != null, "callback cannot be null");
int callingUid = mDep.getCallingUid();
+ RemoteTaskClientServiceConnection connection = null;
+ String uidName;
synchronized (mLock) {
- String uidName = getNameForUidLocked(callingUid);
+ uidName = getNameForUidLocked(callingUid);
Slogf.i(TAG, "removeCarRemoteTaskClient from uid: %s", uidName);
ClientToken token = mClientTokenByUidName.get(uidName);
if (token == null) {
@@ -559,9 +560,16 @@
}
callback.asBinder().unlinkToDeath(token, /* flags= */ 0);
token.setCallback(null);
+ connection = getServiceConnectionLocked(uidName);
}
- // TODO(b/261337288): Shutdown if there are pending remote tasks and no client is
- // registered.
+ if (connection == null) {
+ Slogf.w(TAG, "No active service connection for uid: %s", uidName);
+ return;
+ }
+ connection.removeAllActiveTasks();
+ Slogf.i(TAG, "All active tasks removed for uid: %s, after %d ms, check whether we should "
+ + "shutdown", uidName, mTaskUnbindDelayMs);
+ mHandler.postMaybeShutdown(/* delayMs= */ mTaskUnbindDelayMs);
}
@GuardedBy("mLock")
@@ -965,7 +973,10 @@
synchronized (mLock) {
isReadyForRemoteTask = mIsReadyForRemoteTask;
isWakeupRequired = mIsWakeupRequired;
- if (mNotifyApPowerStateRetryCount == NOTIFY_AP_STATE_MAX_RETRY) {
+ mNotifyApPowerStateRetryCount++;
+ if (mNotifyApPowerStateRetryCount > NOTIFY_AP_STATE_MAX_RETRY) {
+ Slogf.e(TAG, "Reached max retry count for trying to notify AP state change, "
+ + "Failed to notify AP state Change!!!");
return;
}
}
@@ -973,6 +984,14 @@
Slogf.e(TAG, "Cannot notify AP state change, waiting for "
+ NOTIFY_AP_STATE_RETRY_SLEEP_IN_MS + "ms and retry");
mHandler.postNotifyApStateChange(NOTIFY_AP_STATE_RETRY_SLEEP_IN_MS);
+ return;
+ }
+ synchronized (mLock) {
+ mNotifyApPowerStateRetryCount = 0;
+ }
+ if (DEBUG) {
+ Slogf.d(TAG, "Notified AP about new state, isReadyForRemoteTask: %B, "
+ + "isWakeupRequired: %B", isReadyForRemoteTask, isWakeupRequired);
}
}
@@ -1525,21 +1544,33 @@
mActiveTasks.remove(tasks.valueAt(i));
}
if (mActiveTasks.isEmpty()) {
- // All active tasks are completed for this package, we can now unbind the
- // service after mTaskUnbindDelayMs.
- Slogf.i(TAG, "All tasks completed for service: %s", mIntent);
- long currentTimeMs = SystemClock.uptimeMillis();
- if (DEBUG) {
- Slogf.d(TAG, "Unbind remote task client service: %s after %d ms, "
- + "current time: %d ms", mIntent, mTaskUnbindDelayMs,
- currentTimeMs);
- }
- mTaskTimeoutMs = currentTimeMs + mTaskUnbindDelayMs;
- mHandler.postServiceTimeout(mUid, mTaskTimeoutMs);
+ handleAllTasksCompletionLocked();
}
}
return true;
}
+
+ public void removeAllActiveTasks() {
+ synchronized (mServiceLock) {
+ mActiveTasks.clear();
+ handleAllTasksCompletionLocked();
+ }
+ }
+
+ @GuardedBy("mServiceLock")
+ private void handleAllTasksCompletionLocked() {
+ // All active tasks are completed for this package, we can now unbind the service after
+ // mTaskUnbindDelayMs.
+ Slogf.i(TAG, "All tasks completed for service: %s", mIntent);
+ long currentTimeMs = SystemClock.uptimeMillis();
+ if (DEBUG) {
+ Slogf.d(TAG, "Unbind remote task client service: %s after %d ms, "
+ + "current time: %d ms", mIntent, mTaskUnbindDelayMs,
+ currentTimeMs);
+ }
+ mTaskTimeoutMs = currentTimeMs + mTaskUnbindDelayMs;
+ mHandler.postServiceTimeout(mUid, mTaskTimeoutMs);
+ }
}
private static final class RemoteTaskClientServiceHandler extends Handler {
diff --git a/service/src/com/android/car/telemetry/CarTelemetryService.java b/service/src/com/android/car/telemetry/CarTelemetryService.java
index 24431b3..4745554 100644
--- a/service/src/com/android/car/telemetry/CarTelemetryService.java
+++ b/service/src/com/android/car/telemetry/CarTelemetryService.java
@@ -412,10 +412,9 @@
+ " from car telemetry service");
}
mTelemetryThreadTraceLog.traceBegin("removeMetricsConfig");
- if (mMetricsConfigStore.removeMetricsConfig(metricsConfigName)) {
- mDataBroker.removeMetricsConfig(metricsConfigName);
- mResultStore.removeResult(metricsConfigName);
- }
+ mMetricsConfigStore.removeMetricsConfig(metricsConfigName);
+ mDataBroker.removeMetricsConfig(metricsConfigName);
+ mResultStore.removeResult(metricsConfigName);
mTelemetryThreadTraceLog.traceEnd();
});
}
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 5a741f4..118ea60 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -310,6 +310,8 @@
private final CarPackageManagerService mCarPackageManagerService;
+ private final CarOccupantZoneService mCarOccupantZoneService;
+
/**
* Whether some operations - like user switch - are restricted by driving safety constraints.
*/
@@ -347,12 +349,13 @@
@NonNull UserManager userManager,
int maxRunningUsers,
@NonNull CarUxRestrictionsManagerService uxRestrictionService,
- @NonNull CarPackageManagerService carPackageManagerService) {
+ @NonNull CarPackageManagerService carPackageManagerService,
+ @NonNull CarOccupantZoneService carOccupantZoneService) {
this(context, hal, userManager, new UserHandleHelper(context, userManager),
context.getSystemService(DevicePolicyManager.class),
context.getSystemService(ActivityManager.class), maxRunningUsers,
/* initialUserSetter= */ null, uxRestrictionService, /* handler= */ null,
- carPackageManagerService);
+ carPackageManagerService, carOccupantZoneService);
}
@VisibleForTesting
@@ -365,7 +368,8 @@
@Nullable InitialUserSetter initialUserSetter,
@NonNull CarUxRestrictionsManagerService uxRestrictionService,
@Nullable Handler handler,
- @NonNull CarPackageManagerService carPackageManagerService) {
+ @NonNull CarPackageManagerService carPackageManagerService,
+ @NonNull CarOccupantZoneService carOccupantZoneService) {
Slogf.d(TAG, "CarUserService(): DBG=%b, user=%s", DBG, context.getUser());
mContext = context;
mHal = hal;
@@ -386,6 +390,7 @@
mIsVisibleBackgroundUsersOnDefaultDisplaySupported =
isVisibleBackgroundUsersOnDefaultDisplaySupported(mUserManager);
mCreateUserQueue = new ArrayDeque<>(UserManagerHelper.getMaxRunningUsers(context));
+ mCarOccupantZoneService = carOccupantZoneService;
}
/**
@@ -407,10 +412,13 @@
mCarUxRestrictionService.registerUxRestrictionsChangeListener(
mCarUxRestrictionsChangeListener, Display.DEFAULT_DISPLAY);
-
- CarLocalServices.getService(CarOccupantZoneService.class).registerCallback(
- mOccupantZoneCallback);
-
+ // Currently mOccupantZoneCallback does the task to bring up UserPicker only when displays
+ // and user assignments are changed. So it's safe not to register if visible background
+ // users are disabled. But, if we'll add more functionalies in the callback, consider to
+ // move the condition into the callback.
+ if (mIsVisibleBackgroundUsersOnDefaultDisplaySupported) {
+ mCarOccupantZoneService.registerCallback(mOccupantZoneCallback);
+ }
CarServiceHelperWrapper.getInstance().runOnConnection(() ->
setUxRestrictions(mCarUxRestrictionService.getCurrentUxRestrictions()));
}
@@ -445,8 +453,7 @@
mCarUxRestrictionService
.unregisterUxRestrictionsChangeListener(mCarUxRestrictionsChangeListener);
- CarLocalServices.getService(CarOccupantZoneService.class).unregisterCallback(
- mOccupantZoneCallback);
+ mCarOccupantZoneService.unregisterCallback(mOccupantZoneCallback);
}
@Override
@@ -504,12 +511,10 @@
// TODO(b/248608281): clean up.
@Nullable
private OccupantZoneInfo getOccupantZoneForDisplayId(int displayId) {
- CarOccupantZoneService zoneService = CarLocalServices.getService(
- CarOccupantZoneService.class);
- List<OccupantZoneInfo> occupantZoneInfos = zoneService.getAllOccupantZones();
+ List<OccupantZoneInfo> occupantZoneInfos = mCarOccupantZoneService.getAllOccupantZones();
for (int index = 0; index < occupantZoneInfos.size(); index++) {
OccupantZoneInfo occupantZoneInfo = occupantZoneInfos.get(index);
- int[] displays = zoneService.getAllDisplaysForOccupantZone(
+ int[] displays = mCarOccupantZoneService.getAllDisplaysForOccupantZone(
occupantZoneInfo.zoneId);
for (int displayIndex = 0; displayIndex < displays.length; displayIndex++) {
if (displays[displayIndex] == displayId) {
@@ -2149,10 +2154,8 @@
}
}
}
- CarOccupantZoneService occupantZoneService =
- CarLocalServices.getService(CarOccupantZoneService.class);
// If the specified display is not available to start a user on.
- if (occupantZoneService.getUserForDisplayId(displayId)
+ if (mCarOccupantZoneService.getUserForDisplayId(displayId)
!= CarOccupantZoneManager.INVALID_USER_ID) {
return UserStartResponse.STATUS_DISPLAY_UNAVAILABLE;
}
@@ -2490,19 +2493,17 @@
// starts user picker on displays without user allocation exception for on driver main display.
void startUserPicker() {
- CarOccupantZoneService zoneService = CarLocalServices.getService(
- CarOccupantZoneService.class);
int driverZoneId = OccupantZoneInfo.INVALID_ZONE_ID;
- boolean hasDriverZone = zoneService.hasDriverZone();
+ boolean hasDriverZone = mCarOccupantZoneService.hasDriverZone();
if (hasDriverZone) {
- driverZoneId = zoneService.getOccupantZone(
+ driverZoneId = mCarOccupantZoneService.getOccupantZone(
CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
VehicleAreaSeat.SEAT_UNKNOWN).zoneId;
}
// Start user picker on displays without user allocation.
List<OccupantZoneInfo> occupantZoneInfos =
- zoneService.getAllOccupantZones();
+ mCarOccupantZoneService.getAllOccupantZones();
for (int i = 0; i < occupantZoneInfos.size(); i++) {
OccupantZoneInfo occupantZoneInfo = occupantZoneInfos.get(i);
int zoneId = occupantZoneInfo.zoneId;
@@ -2511,13 +2512,13 @@
continue;
}
- int userId = zoneService.getUserForOccupant(zoneId);
+ int userId = mCarOccupantZoneService.getUserForOccupant(zoneId);
if (userId != CarOccupantZoneManager.INVALID_USER_ID) {
// If there is already a user allocated to the zone, skip.
continue;
}
- int displayId = zoneService.getDisplayForOccupant(zoneId,
+ int displayId = mCarOccupantZoneService.getDisplayForOccupant(zoneId,
CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
if (displayId == Display.INVALID_DISPLAY) {
Slogf.e(TAG, "No main display for occupant zone:%d", zoneId);
@@ -2559,9 +2560,7 @@
}
int zoneId = zoneInfo.zoneId;
- CarOccupantZoneService zoneService = CarLocalServices.getService(
- CarOccupantZoneService.class);
- int assignResult = zoneService.assignVisibleUserToOccupantZone(zoneId,
+ int assignResult = mCarOccupantZoneService.assignVisibleUserToOccupantZone(zoneId,
UserHandle.of(userId));
if (assignResult != CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK) {
Slogf.w(TAG,
@@ -2574,17 +2573,15 @@
// Unassigns the invisible user from the occupant zone.
private void unassignInvisibleUserFromZone(@UserIdInt int userId) {
- CarOccupantZoneService zoneService = CarLocalServices.getService(
- CarOccupantZoneService.class);
CarOccupantZoneManager.OccupantZoneInfo zoneInfo =
- zoneService.getOccupantZoneForUser(UserHandle.of(userId));
+ mCarOccupantZoneService.getOccupantZoneForUser(UserHandle.of(userId));
if (zoneInfo == null) {
Slogf.e(TAG, "unassignInvisibleUserFromZone: cannot find occupant zone for user %d",
userId);
return;
}
- int result = zoneService.unassignOccupantZone(zoneInfo.zoneId);
+ int result = mCarOccupantZoneService.unassignOccupantZone(zoneInfo.zoneId);
if (result != CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK) {
Slogf.e(TAG,
"unassignInvisibleUserFromZone: failed to unassign user %d from zone %d,"
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 121667f..86e069d 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -203,6 +203,8 @@
mWatchdogPerfHandler.processPackageChangedIntent(intent);
break;
}
+ default:
+ Slogf.i(TAG, "Ignoring unknown intent %s", intent);
}
}
};
@@ -306,6 +308,10 @@
@Override
public void init() {
+ // TODO(b/266008677): The daemon reads the sendResourceUsageStatsEnabled sysprop at the
+ // moment the CarWatchdogService connects to it. Therefore, the property must be set by
+ // CarWatchdogService before connecting with the CarWatchdog daemon. Set the property to
+ // true to enable the sending of resource usage stats from the daemon.
mWatchdogProcessHandler.init();
mWatchdogPerfHandler.init();
subscribePowerManagementService();
@@ -820,6 +826,8 @@
// ON covers resume.
case CarPowerManager.STATE_ON:
return PowerCycle.POWER_CYCLE_RESUME;
+ default:
+ Slogf.e(TAG, "Invalid power state: %d", powerState);
}
return -1;
}
@@ -830,6 +838,8 @@
return "GARAGE_MODE_OFF";
case GarageMode.GARAGE_MODE_ON:
return "GARAGE_MODE_ON";
+ default:
+ Slogf.e(TAG, "Invalid garage mode: %d", garageMode);
}
return "INVALID";
}
diff --git a/service/src/com/android/car/watchdog/OveruseConfigurationCache.java b/service/src/com/android/car/watchdog/OveruseConfigurationCache.java
index 3c8e6a1..55c4c8b 100644
--- a/service/src/com/android/car/watchdog/OveruseConfigurationCache.java
+++ b/service/src/com/android/car/watchdog/OveruseConfigurationCache.java
@@ -27,6 +27,7 @@
import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
import android.automotive.watchdog.internal.ResourceOveruseConfiguration;
import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
+import android.car.builtin.util.Slogf;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -46,6 +47,7 @@
* setting the cache.
*/
public final class OveruseConfigurationCache {
+ private static final String TAG = OveruseConfigurationCache.class.getSimpleName();
static final PerStateBytes DEFAULT_THRESHOLD =
constructPerStateBytes(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);
@@ -179,6 +181,10 @@
return copyPerStateBytes(threshold);
}
break;
+ default:
+ // THIRD_PARTY and UNKNOWN components are set with the
+ // default thresholds.
+ break;
}
threshold = fetchAppCategorySpecificThresholdLocked(genericPackageName);
if (threshold != null) {
@@ -265,6 +271,9 @@
ioConfig.packageSpecificThresholds, mIoThresholdsByVendorPackages);
setIoThresholdsByAppCategoryTypeLocked(ioConfig.categorySpecificThresholds);
break;
+ default:
+ Slogf.i(TAG, "Ignoring I/O overuse threshold for invalid component type: %d",
+ componentType);
}
}
@@ -282,6 +291,10 @@
mIoThresholdsByAppCategoryType.append(ApplicationCategoryType.MEDIA,
threshold.perStateWriteBytes);
break;
+ default:
+ Slogf.i(TAG,
+ "Ignoring I/O overuse threshold for invalid application category: %s",
+ threshold.name);
}
}
}
diff --git a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
index 8969745..b216977 100644
--- a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
+++ b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
@@ -1107,6 +1107,9 @@
+ "current enabled state is %s", userId, packageName,
toEnabledStateString(currentEnabledState));
return false;
+ default:
+ // COMPONENT_ENABLED_STATE_DEFAULT or other non-disabled states.
+ break;
}
PackageManagerHelper.setApplicationEnabledSettingForUser(packageName,
COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
@@ -2076,6 +2079,9 @@
mLastUidIoUsageSummaryReportedDate = mTimeSource.getCurrentDate();
}
break;
+ default:
+ Slogf.i(TAG, "Skipping pull atom request on invalid watchdog atom tag: %d",
+ atomTag);
}
return PULL_SUCCESS;
}
diff --git a/tests/BugReportApp/AndroidManifest.xml b/tests/BugReportApp/AndroidManifest.xml
index b407e76..64a5eb3 100644
--- a/tests/BugReportApp/AndroidManifest.xml
+++ b/tests/BugReportApp/AndroidManifest.xml
@@ -19,6 +19,7 @@
android:versionCode="14"
android:versionName="1.7.3">
+ <uses-permission android:name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID"/>
<uses-permission android:name="android.car.permission.CAR_DRIVING_STATE"/>
<!-- Allow closing HVAC dialog when showing bugreport activity, used by
ACTION_CLOSE_SYSTEM_DIALOGS. -->
@@ -39,6 +40,7 @@
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
@@ -68,6 +70,14 @@
</intent-filter>
</activity>
+ <activity android:name=".ScreenshotActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:exported="true"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true">
+ <meta-data android:name="distractionOptimized" android:value="true"/>
+ </activity>
+
<service android:name=".BugReportService"
android:foregroundServiceType="specialUse"
android:exported="true">
@@ -79,6 +89,12 @@
</intent-filter>
</service>
+ <service android:name=".ScreenshotService"
+ android:foregroundServiceType="specialUse"
+ android:exported="true">
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="com.android.car.bugreport" />
+ </service>
+
<service android:name="com.android.car.bugreport.UploadJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
diff --git a/tests/BugReportApp/res/layout/bug_info_view.xml b/tests/BugReportApp/res/layout/bug_info_view.xml
index de3da1d..ba95a29 100644
--- a/tests/BugReportApp/res/layout/bug_info_view.xml
+++ b/tests/BugReportApp/res/layout/bug_info_view.xml
@@ -69,15 +69,6 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
- android:id="@+id/bug_info_add_audio_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
- android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
- android:visibility="gone"
- android:text="@string/bugreport_add_audio_button_text"
- style="@style/Widget.BugReportUi.InfoActionButton" />
- <Button
android:id="@+id/bug_info_upload_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -91,9 +82,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
+ android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
android:visibility="gone"
android:text="@string/bugreport_move_button_text"
style="@style/Widget.BugReportUi.InfoActionButton" />
+ <Button
+ android:id="@+id/bug_info_add_audio_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/bug_report_user_action_button_padding"
+ android:layout_marginRight="@dimen/bug_report_horizontal_layout_children_margin"
+ android:visibility="gone"
+ android:text="@string/bugreport_add_audio_button_text"
+ style="@style/Widget.BugReportUi.InfoActionButton" />
</LinearLayout>
</LinearLayout>
diff --git a/tests/BugReportApp/res/layout/bug_report_activity.xml b/tests/BugReportApp/res/layout/bug_report_activity.xml
index 3d0cf3b..5de73d4 100644
--- a/tests/BugReportApp/res/layout/bug_report_activity.xml
+++ b/tests/BugReportApp/res/layout/bug_report_activity.xml
@@ -52,6 +52,14 @@
android:layout_height="@dimen/bug_report_voice_recording_height"
android:layout_width="match_parent"/>
<TextView
+ android:id="@+id/voice_recording_timer_text_view"
+ android:layout_marginTop="@dimen/bug_report_voice_recording_margin_top"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bug_report_voice_recording_height"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/bugreport_text"
+ android:gravity="center"/>
+ <TextView
android:id="@+id/voice_recording_finished_text_view"
android:layout_marginTop="@dimen/bug_report_voice_recording_margin_top"
android:layout_width="match_parent"
@@ -75,6 +83,13 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bug_report_button_margin_top"
android:text="@string/bugreport_dialog_cancel"/>
+ <Button
+ android:id="@+id/button_record_again"
+ style="@style/standard_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/bug_report_button_margin_top"
+ android:text="@string/bugreport_dialog_record_again"/>
</LinearLayout>
<LinearLayout
diff --git a/tests/BugReportApp/res/values/strings.xml b/tests/BugReportApp/res/values/strings.xml
index 34ec183..513e2a8 100644
--- a/tests/BugReportApp/res/values/strings.xml
+++ b/tests/BugReportApp/res/values/strings.xml
@@ -24,6 +24,7 @@
<string name="bugreport_dialog_submit">Submit</string>
<string name="bugreport_dialog_cancel">Cancel</string>
+ <string name="bugreport_dialog_record_again">Record Again</string>
<!-- A button: uploads bugreport with recorded audio message. -->
<string name="bugreport_dialog_upload">Upload</string>
<!-- A button: saves recorded audio message. -->
@@ -36,8 +37,8 @@
<string name="bugreport_dialog_recording_finished">Recording finished</string>
<string name="bugreport_dialog_in_progress_title">Bug report is in progress</string>
<string name="bugreport_dialog_in_progress_title_finished">A bug report has been collected</string>
- <!-- A button to add audio message to the bugreport. It will show Save button on the dialog. -->
<string name="bugreport_add_audio_button_text">Add Audio</string>
+ <string name="bugreport_replace_audio_button_text">Replace Audio</string>
<!-- A button to add audio message to the bugreport; it will show Upload button on the dialog. -->
<string name="bugreport_add_audio_upload_button_text">Add Audio & Upload</string>
<string name="bugreport_move_button_text">Move to USB</string>
@@ -50,9 +51,12 @@
<string name="toast_status_failed">Bug report failed</string>
<string name="toast_status_screencap_failed">Screen capture failed</string>
<string name="toast_status_dump_state_failed">Dumpstate failed</string>
+ <string name="toast_screenshot_finished">Saved screenshot(s)</string>
<!-- Notification strings -->
<string name="notification_bugreport_in_progress">Bug report is in progress</string>
<string name="notification_bugreport_finished_title">Bug report has been collected</string>
<string name="notification_bugreport_channel_name">Bug report status channel</string>
+ <string name="notification_screenshot_message">Taking a screenshot</string>
+ <string name="notification_screenshot_channel_name">Bug report screenshot channel</string>
</resources>
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/BugInfoAdapter.java b/tests/BugReportApp/src/com/android/car/bugreport/BugInfoAdapter.java
index 12051c7..8b33d96 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/BugInfoAdapter.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/BugInfoAdapter.java
@@ -38,19 +38,19 @@
static final int BUTTON_TYPE_MOVE = 1;
static final int BUTTON_TYPE_ADD_AUDIO = 2;
- /** If bugreport TTL points go below this number, show a notice message. */
+ /** If bugreport TTL points go below this number, show a notice message. */
private static final int MIN_TTL_POINTS_TO_SHOW_NOTICE = 10;
- /** Provides a handler for click events*/
+ /** Provides a handler for click events. */
interface ItemClickedListener {
/**
* Handles click events differently depending on provided buttonType and
* uses additional information provided in metaBugReport.
*
- * @param buttonType One of {@link #BUTTON_TYPE_UPLOAD}, {@link #BUTTON_TYPE_MOVE} or
- * {@link #BUTTON_TYPE_ADD_AUDIO}.
+ * @param buttonType One of {@link #BUTTON_TYPE_UPLOAD}, {@link #BUTTON_TYPE_MOVE} or
+ * {@link #BUTTON_TYPE_ADD_AUDIO}.
* @param metaBugReport Selected bugreport.
- * @param holder ViewHolder of the clicked item.
+ * @param holder ViewHolder of the clicked item.
*/
void onItemClicked(int buttonType, MetaBugReport metaBugReport, BugInfoViewHolder holder);
}
@@ -114,57 +114,95 @@
@Override
public void onBindViewHolder(BugInfoViewHolder holder, int position) {
- MetaBugReport bugreport = mDataset.get(position);
- holder.mTitleView.setText(bugreport.getTitle());
- holder.mStatusView.setText(Status.toString(bugreport.getStatus()));
- holder.mMessageView.setText(bugreport.getStatusMessage());
- if (bugreport.getStatusMessage().isEmpty()) {
+ MetaBugReport bugReport = mDataset.get(position);
+ holder.mTitleView.setText(bugReport.getTitle());
+ holder.mStatusView.setText(Status.toString(bugReport.getStatus()));
+
+ showOrHideMessageView(bugReport, holder);
+ showOrHideExpirationNotice(bugReport, holder);
+ showOrHideUploadButton(bugReport, holder);
+ showOrHideMoveButton(bugReport, holder);
+ showOrHideAddAudioButton(bugReport, holder);
+ }
+
+ private boolean pendingUserAction(MetaBugReport bugReport) {
+ int bugReportStatus = bugReport.getStatus();
+ return bugReportStatus == Status.STATUS_PENDING_USER_ACTION.getValue()
+ || bugReportStatus == Status.STATUS_MOVE_FAILED.getValue()
+ || bugReportStatus == Status.STATUS_UPLOAD_FAILED.getValue();
+ }
+
+ private boolean pendingAudioRecording(MetaBugReport bugReport) {
+ return bugReport.getStatus() == Status.STATUS_AUDIO_PENDING.getValue();
+ }
+
+ private void showOrHideMessageView(MetaBugReport bugReport, BugInfoViewHolder holder) {
+ holder.mMessageView.setText(bugReport.getStatusMessage());
+ if (bugReport.getStatusMessage().isEmpty()) {
holder.mMessageView.setVisibility(View.GONE);
} else {
holder.mMessageView.setVisibility(View.VISIBLE);
}
- if (bugreport.getTtlPoints() <= MIN_TTL_POINTS_TO_SHOW_NOTICE
- && BugStorageUtils.canBugReportBeExpired(bugreport.getStatus())) {
+ }
+
+ private void showOrHideExpirationNotice(MetaBugReport bugReport, BugInfoViewHolder holder) {
+ if (bugReport.getTtlPoints() <= MIN_TTL_POINTS_TO_SHOW_NOTICE
+ && BugStorageUtils.canBugReportBeExpired(bugReport.getStatus())) {
holder.mExpirationNoticeView.setVisibility(View.VISIBLE);
} else {
holder.mExpirationNoticeView.setVisibility(View.GONE);
}
- boolean enableUserActionButtons =
- bugreport.getStatus() == Status.STATUS_PENDING_USER_ACTION.getValue()
- || bugreport.getStatus() == Status.STATUS_MOVE_FAILED.getValue()
- || bugreport.getStatus() == Status.STATUS_UPLOAD_FAILED.getValue();
- if (enableUserActionButtons) {
- holder.mMoveButton.setEnabled(true);
- holder.mMoveButton.setVisibility(View.VISIBLE);
- holder.mMoveButton.setOnClickListener(
- view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_MOVE, bugreport,
- holder));
- } else {
- holder.mMoveButton.setEnabled(false);
- holder.mMoveButton.setVisibility(View.GONE);
- }
+ }
+
+ private void showOrHideUploadButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
// Enable the upload button only for userdebug/eng builds.
- if (enableUserActionButtons && Build.IS_DEBUGGABLE) {
+ if (pendingUserAction(bugReport) && Build.IS_DEBUGGABLE) {
holder.mUploadButton.setText(R.string.bugreport_upload_gcs_button_text);
holder.mUploadButton.setEnabled(true);
holder.mUploadButton.setVisibility(View.VISIBLE);
holder.mUploadButton.setOnClickListener(
- view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_UPLOAD, bugreport,
+ view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_UPLOAD, bugReport,
holder));
} else {
holder.mUploadButton.setVisibility(View.GONE);
holder.mUploadButton.setEnabled(false);
}
- if (bugreport.getStatus() == Status.STATUS_AUDIO_PENDING.getValue()) {
+ }
+
+ private void showOrHideMoveButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
+ if (pendingUserAction(bugReport)) {
+ holder.mMoveButton.setEnabled(true);
+ holder.mMoveButton.setVisibility(View.VISIBLE);
+ holder.mMoveButton.setOnClickListener(
+ view -> mItemClickedListener.onItemClicked(BUTTON_TYPE_MOVE, bugReport,
+ holder));
+ } else {
+ holder.mMoveButton.setEnabled(false);
+ holder.mMoveButton.setVisibility(View.GONE);
+ }
+ }
+
+ private void showOrHideAddAudioButton(MetaBugReport bugReport, BugInfoViewHolder holder) {
+ boolean showButton = true;
+
+ if (pendingAudioRecording(bugReport)) {
if (mConfig.isAutoUpload()) {
- holder.mAddAudioButton.setText(R.string.bugreport_add_audio_upload_button_text);
+ holder.mAddAudioButton.setText(
+ R.string.bugreport_add_audio_upload_button_text);
} else {
holder.mAddAudioButton.setText(R.string.bugreport_add_audio_button_text);
}
+ } else if (pendingUserAction(bugReport)) {
+ holder.mAddAudioButton.setText(R.string.bugreport_replace_audio_button_text);
+ } else {
+ showButton = false;
+ }
+
+ if (showButton) {
holder.mAddAudioButton.setEnabled(true);
holder.mAddAudioButton.setVisibility(View.VISIBLE);
holder.mAddAudioButton.setOnClickListener(view ->
- mItemClickedListener.onItemClicked(BUTTON_TYPE_ADD_AUDIO, bugreport, holder));
+ mItemClickedListener.onItemClicked(BUTTON_TYPE_ADD_AUDIO, bugReport, holder));
} else {
holder.mAddAudioButton.setEnabled(false);
holder.mAddAudioButton.setVisibility(View.GONE);
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/BugReportActivity.java b/tests/BugReportApp/src/com/android/car/bugreport/BugReportActivity.java
index c1fbbcb..50a0ec1 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/BugReportActivity.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/BugReportActivity.java
@@ -40,6 +40,7 @@
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -53,6 +54,7 @@
import android.widget.Toast;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.io.ByteStreams;
@@ -63,6 +65,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
+import java.util.Locale;
import java.util.Objects;
import java.util.Random;
@@ -111,6 +114,7 @@
private ProgressBar mProgressBar;
private TextView mProgressText;
private TextView mAddAudioText;
+ private TextView mTimerText;
private VoiceRecordingView mVoiceRecordingView;
private View mVoiceRecordingFinishedView;
private View mSubmitBugReportLayout;
@@ -129,12 +133,13 @@
private BugReportService mService;
private MediaRecorder mRecorder;
private MetaBugReport mMetaBugReport;
- private File mAudioFile;
+ private File mTempAudioFile;
private Car mCar;
private CarDrivingStateManager mDrivingStateManager;
private AudioManager mAudioManager;
private AudioFocusRequest mLastAudioFocusRequest;
private Config mConfig;
+ private CountDownTimer mCountDownTimer;
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@@ -157,11 +162,11 @@
* Builds an intent that starts {@link BugReportActivity} to add audio message to the existing
* bug report.
*/
- static Intent buildAddAudioIntent(Context context, MetaBugReport bug) {
+ static Intent buildAddAudioIntent(Context context, int bugReportId) {
Intent addAudioIntent = new Intent(context, BugReportActivity.class);
addAudioIntent.setAction(ACTION_ADD_AUDIO);
addAudioIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- addAudioIntent.putExtra(EXTRA_BUGREPORT_ID, bug.getId());
+ addAudioIntent.putExtra(EXTRA_BUGREPORT_ID, bugReportId);
return addAudioIntent;
}
@@ -211,14 +216,11 @@
mIsSubmitButtonClicked = false;
mIsOnActivityStartedWithBugReportServiceBoundCalled = false;
mMetaBugReport = null;
- mAudioFile = null;
+ mTempAudioFile = null;
}
@Override
public void onDestroy() {
- if (mRecorder != null) {
- mHandler.removeCallbacksAndMessages(/* token= */ mRecorder);
- }
if (mBound) {
unbindService(mConnection);
mBound = false;
@@ -275,6 +277,7 @@
mProgressText = findViewById(R.id.progress_text);
mAddAudioText = findViewById(R.id.bug_report_add_audio_to_existing);
mVoiceRecordingView = findViewById(R.id.voice_recording_view);
+ mTimerText = findViewById(R.id.voice_recording_timer_text_view);
mVoiceRecordingFinishedView = findViewById(R.id.voice_recording_finished_text_view);
mSubmitBugReportLayout = findViewById(R.id.submit_bug_report_layout);
mInProgressLayout = findViewById(R.id.in_progress_layout);
@@ -285,6 +288,7 @@
mSubmitButton.setOnClickListener(this::buttonSubmitClick);
findViewById(R.id.button_cancel).setOnClickListener(this::buttonCancelClick);
findViewById(R.id.button_close).setOnClickListener(this::buttonCancelClick);
+ findViewById(R.id.button_record_again).setOnClickListener(this::buttonRecordAgainClick);
if (mIsNewBugReport) {
mSubmitButton.setText(R.string.bugreport_dialog_submit);
@@ -328,9 +332,11 @@
if (isRecording) {
mVoiceRecordingFinishedView.setVisibility(View.GONE);
mVoiceRecordingView.setVisibility(View.VISIBLE);
+ mTimerText.setVisibility(View.VISIBLE);
} else {
mVoiceRecordingFinishedView.setVisibility(View.VISIBLE);
mVoiceRecordingView.setVisibility(View.GONE);
+ mTimerText.setVisibility(View.GONE);
}
// NOTE: mShowBugReportsButton visibility is also handled in #onCarDrivingStateChanged().
mShowBugReportsButton.setVisibility(View.GONE);
@@ -381,36 +387,38 @@
return AUDIO_FILE_EXTENSION_3GPP;
}
- private void addAudioToExistingBugReport(int bugreportId) {
- MetaBugReport bug = BugStorageUtils.findBugReport(this, bugreportId).orElseThrow(
- () -> new RuntimeException("Failed to find bug report with id " + bugreportId));
- Log.i(TAG, "Adding audio to the existing bugreport " + bug.getTimestamp());
- if (bug.getStatus() != Status.STATUS_AUDIO_PENDING.getValue()) {
- Log.e(TAG, "Failed to add audio, bad status, expected "
- + Status.STATUS_AUDIO_PENDING.getValue() + ", got " + bug.getStatus());
- finish();
- }
- File audioFile;
- try {
- audioFile = File.createTempFile("audio", "." + getAudioFileExtension(), getCacheDir());
- } catch (IOException e) {
- throw new RuntimeException("failed to create temp audio file", e);
- }
- startAudioMessageRecording(/* isNewBugReport= */ false, bug, audioFile);
+ private void addAudioToExistingBugReport(int existingBugReportId) {
+ MetaBugReport existingBugReport = BugStorageUtils.findBugReport(this,
+ existingBugReportId).orElseThrow(() -> new RuntimeException(
+ "Failed to find bug report with id " + existingBugReportId));
+ Log.i(TAG, "Adding audio to the existing bugreport " + existingBugReport.getTimestamp());
+ startAudioMessageRecording(/* isNewBugReport= */ false, existingBugReport,
+ createTempAudioFileInCacheDirectory());
}
private void createNewBugReportWithAudioMessage() {
- String audioFileSuffix = "-message." + getAudioFileExtension();
- MetaBugReport bug = createBugReport(this, MetaBugReport.TYPE_AUDIO_FIRST);
- startAudioMessageRecording(
- /* isNewBugReport= */ true,
- bug,
- FileUtils.getFileWithSuffix(this, bug.getTimestamp(), audioFileSuffix));
+ MetaBugReport newBugReport = createBugReport(this, MetaBugReport.TYPE_AUDIO_FIRST);
+ startAudioMessageRecording(/* isNewBugReport= */ true, newBugReport,
+ createTempAudioFileInCacheDirectory());
+ }
+
+ /**
+ * Creates a temporary audio file in cache directory for voice recording.
+ *
+ * For example, /data/user/10/com.android.car.bugreport/cache/audio1128264677920904030.wav
+ */
+ private File createTempAudioFileInCacheDirectory() {
+ try {
+ return File.createTempFile("audio", "." + getAudioFileExtension(),
+ getCacheDir());
+ } catch (IOException e) {
+ throw new RuntimeException("failed to create temp audio file", e);
+ }
}
/** Shows a dialog UI and starts recording audio message. */
private void startAudioMessageRecording(
- boolean isNewBugReport, MetaBugReport bug, File audioFile) {
+ boolean isNewBugReport, MetaBugReport bugReport, File tempAudioFile) {
if (mAudioRecordingStarted) {
Log.i(TAG, "Audio message recording is already started.");
return;
@@ -422,8 +430,8 @@
mAudioManager = getSystemService(AudioManager.class);
mIsNewBugReport = isNewBugReport;
- mMetaBugReport = bug;
- mAudioFile = audioFile;
+ mMetaBugReport = bugReport;
+ mTempAudioFile = tempAudioFile;
prepareUi();
showSubmitBugReportUi(/* isRecording= */ true);
if (isNewBugReport) {
@@ -454,7 +462,7 @@
}
/**
- * Cancels bugreporting by stopping audio recording and deleting temp files.
+ * Cancels bugreporting by stopping audio recording and deleting temp audio file.
*/
private void cancelAudioMessageRecording() {
// If audio recording is not running, most likely there were permission issues,
@@ -465,16 +473,11 @@
}
stopAudioRecording();
if (mIsNewBugReport) {
- // It creates a temp dir only for new TYPE_AUDIO_FIRST bugreports only.
- File tempDir = FileUtils.getTempDir(this, mMetaBugReport.getTimestamp());
- new DeleteFilesAndDirectoriesAsyncTask().execute(tempDir);
- } else {
- BugStorageUtils.deleteBugReportFiles(this, mMetaBugReport.getId());
- new DeleteFilesAndDirectoriesAsyncTask().execute(mAudioFile);
+ BugStorageUtils.setBugReportStatus(
+ this, mMetaBugReport, Status.STATUS_USER_CANCELLED, "");
+ Log.i(TAG, "Bug report " + mMetaBugReport.getTimestamp() + " is cancelled");
}
- BugStorageUtils.setBugReportStatus(
- this, mMetaBugReport, Status.STATUS_USER_CANCELLED, "");
- Log.i(TAG, "Bug report " + mMetaBugReport.getTimestamp() + " is cancelled");
+ new DeleteFilesAndDirectoriesAsyncTask().execute(mTempAudioFile);
mAudioRecordingStarted = false;
mAudioRecordingIsRunning = false;
}
@@ -483,29 +486,23 @@
finish();
}
+ private void buttonRecordAgainClick(View view) {
+ stopAudioRecording();
+ showSubmitBugReportUi(/* isRecording= */ true);
+ startRecordingWithPermission();
+ }
+
private void buttonSubmitClick(View view) {
stopAudioRecording();
mIsSubmitButtonClicked = true;
- if (mIsNewBugReport) {
- Log.i(TAG, "Starting bugreport service.");
- startBugReportCollection(mMetaBugReport);
- } else {
- Log.i(TAG, "Adding audio file to the bugreport " + mMetaBugReport.getTimestamp());
- new AddAudioToBugReportAsyncTask(this, mConfig, mMetaBugReport, mAudioFile).execute();
- }
+
+ new AddAudioToBugReportAsyncTask(this, mConfig, mMetaBugReport, mTempAudioFile,
+ mIsNewBugReport).execute();
+
setResult(Activity.RESULT_OK);
finish();
}
- /** Starts the {@link BugReportService} to collect bug report. */
- private void startBugReportCollection(MetaBugReport bug) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(BugReportService.EXTRA_META_BUG_REPORT, bug);
- Intent intent = new Intent(this, BugReportService.class);
- intent.setAction(BugReportService.ACTION_COLLECT_BUGREPORT);
- intent.putExtras(bundle);
- startForegroundService(intent);
- }
/**
* Starts {@link BugReportInfoActivity} and finishes current activity, so it won't be running
@@ -593,12 +590,12 @@
Log.i(TAG, "OnMediaRecorderInfo: what=" + what + ", extra=" + extra));
mediaRecorder.setOnErrorListener((MediaRecorder recorder, int what, int extra) ->
Log.i(TAG, "OnMediaRecorderError: what=" + what + ", extra=" + extra));
- mediaRecorder.setOutputFile(mAudioFile);
+ mediaRecorder.setOutputFile(mTempAudioFile);
return mediaRecorder;
}
private void startRecordingWithPermission() {
- Log.i(TAG, "Started voice recording, and saving audio to " + mAudioFile);
+ Log.i(TAG, "Started voice recording, and saving audio to " + mTempAudioFile);
mLastAudioFocusRequest = new AudioFocusRequest.Builder(
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
@@ -621,27 +618,40 @@
try {
mRecorder.prepare();
} catch (IOException e) {
- Log.e(TAG, "Failed on MediaRecorder#prepare(), filename: " + mAudioFile, e);
+ Log.e(TAG, "Failed on MediaRecorder#prepare(), filename: " + mTempAudioFile, e);
finish();
return;
}
+ mCountDownTimer = createCountDownTimer();
+ mCountDownTimer.start();
+
mRecorder.start();
mVoiceRecordingView.setRecorder(mRecorder);
mAudioRecordingIsRunning = true;
+ }
- // Messages with token mRecorder are cleared when the activity finishes or recording stops.
- mHandler.postDelayed(() -> {
- Log.i(TAG, "Timed out while recording voice message, cancelling.");
- stopAudioRecording();
- showSubmitBugReportUi(/* isRecording= */ false);
- }, /* token= */ mRecorder, VOICE_MESSAGE_MAX_DURATION_MILLIS);
+ private CountDownTimer createCountDownTimer() {
+ return new CountDownTimer(VOICE_MESSAGE_MAX_DURATION_MILLIS, 1000) {
+ public void onTick(long millisUntilFinished) {
+ long secondsRemaining = millisUntilFinished / 1000;
+ String secondText = secondsRemaining > 1 ? "seconds" : "second";
+ mTimerText.setText(String.format(Locale.US, "%d %s remaining", secondsRemaining,
+ secondText));
+ }
+
+ public void onFinish() {
+ Log.i(TAG, "Timed out while recording voice message.");
+ stopAudioRecording();
+ showSubmitBugReportUi(/* isRecording= */ false);
+ }
+ };
}
private void stopAudioRecording() {
+ mCountDownTimer.cancel();
if (mRecorder != null) {
Log.i(TAG, "Recording ended, stopping the MediaRecorder.");
- mHandler.removeCallbacksAndMessages(/* token= */ mRecorder);
try {
mRecorder.stop();
} catch (RuntimeException e) {
@@ -726,53 +736,93 @@
}
/**
- * AsyncTask that moves audio file to the system user's {@link FileUtils#getPendingDir} and
- * sets status to either STATUS_UPLOAD_PENDING or STATUS_PENDING_USER_ACTION.
+ * AsyncTask that moves temp audio file to the system user's {@link FileUtils#getPendingDir}.
+ * Once the task is completed, it either starts ACTION_COLLECT_BUGREPORT or updates the status
+ * to STATUS_UPLOAD_PENDING or STATUS_PENDING_USER_ACTION.
*/
private static class AddAudioToBugReportAsyncTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
private final Config mConfig;
- private final File mAudioFile;
- private final MetaBugReport mOriginalBug;
+ private final File mTempAudioFile;
+ private boolean mIsNewBugReport;
+ private final boolean mIsFirstRecording;
+ private MetaBugReport mBugReport;
AddAudioToBugReportAsyncTask(
- Context context, Config config, MetaBugReport bug, File audioFile) {
+ Context context, Config config, MetaBugReport bugReport, File tempAudioFile,
+ boolean isNewBugReport) {
mContext = context;
mConfig = config;
- mOriginalBug = bug;
- mAudioFile = audioFile;
+ mBugReport = bugReport;
+ mTempAudioFile = tempAudioFile;
+ mIsNewBugReport = isNewBugReport;
+ mIsFirstRecording = Strings.isNullOrEmpty(mBugReport.getAudioFileName());
}
@Override
protected Void doInBackground(Void... voids) {
- String audioFileExtension = mAudioFile.getName().substring(
- mAudioFile.getName().lastIndexOf(".") + 1);
- String audioTimestamp = MetaBugReport.toBugReportTimestamp(new Date());
- String audioFileName = FileUtils.getAudioFileName(
- audioTimestamp, mOriginalBug, audioFileExtension);
- MetaBugReport bug = BugStorageUtils.update(mContext,
- mOriginalBug.toBuilder().setAudioFileName(audioFileName).build());
- try (OutputStream out = BugStorageUtils.openAudioMessageFileToWrite(mContext, bug);
- InputStream input = new FileInputStream(mAudioFile)) {
+ String audioFileName = createFinalAudioFileName();
+ mBugReport = BugStorageUtils.update(mContext,
+ mBugReport.toBuilder().setAudioFileName(audioFileName).build());
+ try (OutputStream out = BugStorageUtils.openAudioMessageFileToWrite(mContext,
+ mBugReport);
+ InputStream input = new FileInputStream(mTempAudioFile)) {
ByteStreams.copy(input, out);
} catch (IOException e) {
// Allow user to try again if it fails to write audio.
- BugStorageUtils.setBugReportStatus(mContext, bug,
+ BugStorageUtils.setBugReportStatus(mContext, mBugReport,
com.android.car.bugreport.Status.STATUS_AUDIO_PENDING,
"Failed to write audio to bug report");
Log.e(TAG, "Failed to write audio to bug report", e);
return null;
}
- if (mConfig.isAutoUpload()) {
- BugStorageUtils.setBugReportStatus(mContext, bug,
- com.android.car.bugreport.Status.STATUS_UPLOAD_PENDING, "");
- } else {
- BugStorageUtils.setBugReportStatus(mContext, bug,
- com.android.car.bugreport.Status.STATUS_PENDING_USER_ACTION, "");
- BugReportService.showBugReportFinishedNotification(mContext, bug);
- }
- mAudioFile.delete();
+
+ mTempAudioFile.delete();
return null;
}
+
+ /**
+ * Creates a final audio file name from temp audio file.
+ *
+ * For example,
+ * audio1128264677920904030.wav -> bugreport-Driver@2023-07-03_02-55-12-TLBZUR-message.wav
+ */
+ private String createFinalAudioFileName() {
+ String audioFileExtension = mTempAudioFile.getName().substring(
+ mTempAudioFile.getName().lastIndexOf(".") + 1);
+ String audioTimestamp = MetaBugReport.toBugReportTimestamp(new Date());
+ return FileUtils.getAudioFileName(audioTimestamp, mBugReport, audioFileExtension);
+ }
+
+ @Override
+ protected void onPostExecute(Void unused) {
+ super.onPostExecute(unused);
+ if (mIsNewBugReport) {
+ Log.i(TAG, "Starting bugreport service.");
+ startBugReportCollection(mBugReport.getId());
+ } else {
+ if (mConfig.isAutoUpload()) {
+ BugStorageUtils.setBugReportStatus(mContext, mBugReport,
+ com.android.car.bugreport.Status.STATUS_UPLOAD_PENDING, "");
+ } else {
+ BugStorageUtils.setBugReportStatus(mContext, mBugReport,
+ com.android.car.bugreport.Status.STATUS_PENDING_USER_ACTION, "");
+
+ // If audio file name already exists, no need to show the finish notification
+ // again for audio replacement.
+ if (mIsFirstRecording) {
+ BugReportService.showBugReportFinishedNotification(mContext, mBugReport);
+ }
+ }
+ }
+ }
+
+ /** Starts the {@link BugReportService} to collect bug report. */
+ private void startBugReportCollection(int bugReportId) {
+ Intent intent = new Intent(mContext, BugReportService.class);
+ intent.setAction(BugReportService.ACTION_COLLECT_BUGREPORT);
+ intent.putExtra(BugReportService.EXTRA_META_BUG_REPORT_ID, bugReportId);
+ mContext.startForegroundService(intent);
+ }
}
}
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/BugReportInfoActivity.java b/tests/BugReportApp/src/com/android/car/bugreport/BugReportInfoActivity.java
index eacaa85..6f46150 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/BugReportInfoActivity.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/BugReportInfoActivity.java
@@ -75,7 +75,6 @@
private BugInfoAdapter.BugInfoViewHolder mLastSelectedBugInfoViewHolder;
private BugStorageObserver mBugStorageObserver;
private Config mConfig;
- private boolean mAudioRecordingStarted;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -147,12 +146,8 @@
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE),
SELECT_DIRECTORY_REQUEST_CODE);
} else if (buttonType == BugInfoAdapter.BUTTON_TYPE_ADD_AUDIO) {
- // Check mAudioRecordingStarted to prevent double click to BUTTON_TYPE_ADD_AUDIO.
- if (!mAudioRecordingStarted) {
- mAudioRecordingStarted = true;
- startActivityForResult(BugReportActivity.buildAddAudioIntent(this, bugReport),
- ADD_AUDIO_MESSAGE_REQUEST_CODE);
- }
+ startActivityForResult(BugReportActivity.buildAddAudioIntent(this, bugReport.getId()),
+ ADD_AUDIO_MESSAGE_REQUEST_CODE);
} else {
throw new IllegalStateException("unreachable");
}
@@ -176,7 +171,7 @@
mBugInfoAdapter.updateBugReportInDataSet(
updatedBugReport, mLastSelectedBugInfoViewHolder.getAdapterPosition());
new AsyncMoveFilesTask(
- this,
+ this,
mBugInfoAdapter,
updatedBugReport,
mLastSelectedBugInfoViewHolder,
@@ -197,10 +192,10 @@
* you run "adb shell dumpsys activity BugReportInfoActivity".
*
* @param prefix Desired prefix to prepend at each line of output.
- * @param fd The raw file descriptor that the dump is being sent to.
+ * @param fd The raw file descriptor that the dump is being sent to.
* @param writer The PrintWriter to which you should dump your state. This will be
- * closed for you after you return.
- * @param args additional arguments to the dump request.
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
*/
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
@@ -242,8 +237,8 @@
Log.e(TAG, "Failed to copy bugreport "
+ mBugReport.getTimestamp() + " to USB", e);
return BugStorageUtils.setBugReportStatus(
- mActivity, mBugReport,
- com.android.car.bugreport.Status.STATUS_MOVE_FAILED, e);
+ mActivity, mBugReport,
+ com.android.car.bugreport.Status.STATUS_MOVE_FAILED, e);
}
}
@@ -370,7 +365,7 @@
* Creates a content observer.
*
* @param activity A {@link BugReportInfoActivity} instance.
- * @param handler The handler to run {@link #onChange} on, or null if none.
+ * @param handler The handler to run {@link #onChange} on, or null if none.
*/
BugStorageObserver(BugReportInfoActivity activity, Handler handler) {
super(handler);
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/BugReportService.java b/tests/BugReportApp/src/com/android/car/bugreport/BugReportService.java
index 2243a6d..72ff49a 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/BugReportService.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/BugReportService.java
@@ -43,7 +43,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -62,6 +61,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -86,11 +87,11 @@
/**
* Extra data from intent - current bug report.
*/
- static final String EXTRA_META_BUG_REPORT = "meta_bug_report";
+ static final String EXTRA_META_BUG_REPORT_ID = "meta_bug_report_id";
/**
* Collects bugreport for the existing {@link MetaBugReport}, which must be provided using
- * {@link EXTRA_META_BUG_REPORT}.
+ * {@link EXTRA_META_BUG_REPORT_ID}.
*/
static final String ACTION_COLLECT_BUGREPORT =
"com.android.car.bugreport.action.COLLECT_BUGREPORT";
@@ -257,8 +258,9 @@
mMetaBugReport =
BugReportActivity.createBugReport(this, MetaBugReport.TYPE_AUDIO_LATER);
} else if (ACTION_COLLECT_BUGREPORT.equals(action)) {
- Bundle extras = intent.getExtras();
- mMetaBugReport = extras.getParcelable(EXTRA_META_BUG_REPORT);
+ int bugReportId = intent.getIntExtra(EXTRA_META_BUG_REPORT_ID, /* defaultValue= */ -1);
+ mMetaBugReport = BugStorageUtils.findBugReport(this, bugReportId).orElseThrow(
+ () -> new RuntimeException("Failed to find bug report with id " + bugReportId));
} else {
Log.w(TAG, "No action provided, ignoring");
return START_NOT_STICKY;
@@ -314,7 +316,7 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent startBugReportInfoActivity =
PendingIntent.getActivity(getApplicationContext(), /* requestCode= */ 0, intent,
- PendingIntent.FLAG_IMMUTABLE);
+ PendingIntent.FLAG_IMMUTABLE);
return new Notification.Builder(this, PROGRESS_CHANNEL_ID)
.setContentTitle(getText(R.string.notification_bugreport_in_progress))
.setContentText(mMetaBugReport.getTitle())
@@ -412,7 +414,7 @@
try (ParcelFileDescriptor outFd = ParcelFileDescriptor.open(outputFile,
ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_READ_WRITE);
ParcelFileDescriptor extraOutFd = ParcelFileDescriptor.open(extraOutputFile,
- ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_READ_WRITE)) {
+ ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_READ_WRITE)) {
requestBugReport(outFd, extraOutFd);
} catch (IOException | RuntimeException e) {
Log.e(TAG, "Failed to grab dump state", e);
@@ -525,11 +527,38 @@
.notify(BUGREPORT_FINISHED_NOTIF_ID, notification);
}
+ /** Moves extra screenshots from a screenshot directory to a given directory. */
+ private void moveExtraScreenshots(File destinationDir) {
+ String screenshotDirPath = ScreenshotUtils.getScreenshotDir();
+ if (screenshotDirPath == null) {
+ return;
+ }
+ File screenshotDir = new File(screenshotDirPath);
+ if (!screenshotDir.isDirectory()) {
+ return;
+ }
+ for (File file : screenshotDir.listFiles()) {
+ if (file.isDirectory()) {
+ continue;
+ }
+ String destinationPath = destinationDir.getPath() + "/" + file.getName();
+ try {
+ Files.move(Paths.get(file.getPath()), Paths.get(destinationPath));
+ Log.i(TAG, "Move a screenshot" + file.getPath() + " to " + destinationPath);
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot move a screenshot" + file.getName() + " to bugreport.", e);
+ }
+ }
+ }
+
/**
* Zips the temp directory, writes to the system user's {@link FileUtils#getPendingDir} and
- * updates the bug report status.
+ * updates the bug report status. Note that audio file is always stored in cache directory and
+ * moved by {@link com.android.car.bugreport.BugReportActivity.AddAudioToBugReportAsyncTask}, so
+ * not zipped by this method.
*
- * <p>For {@link MetaBugReport#TYPE_AUDIO_FIRST}: Sets status to either STATUS_UPLOAD_PENDING or
+ * <p>For {@link MetaBugReport#TYPE_AUDIO_FIRST}: Sets status to either STATUS_UPLOAD_PENDING
+ * or
* STATUS_PENDING_USER_ACTION and shows a regular notification.
*
* <p>For {@link MetaBugReport#TYPE_AUDIO_LATER}: Sets status to STATUS_AUDIO_PENDING and shows
@@ -543,7 +572,11 @@
Log.d(TAG, "Zipping bugreport into " + bugreportFileName);
mMetaBugReport = BugStorageUtils.update(this,
mMetaBugReport.toBuilder().setBugReportFileName(bugreportFileName).build());
- File bugReportTempDir = FileUtils.createTempDir(this, mMetaBugReport.getTimestamp());
+ File bugReportTempDir = FileUtils.getTempDir(this, mMetaBugReport.getTimestamp());
+
+ Log.d(TAG, "Adding extra screenshots into " + bugReportTempDir.getAbsolutePath());
+ moveExtraScreenshots(bugReportTempDir);
+
zipDirectoryToOutputStream(bugReportTempDir,
BugStorageUtils.openBugReportFileToWrite(this, mMetaBugReport));
} catch (IOException e) {
@@ -557,7 +590,7 @@
BugStorageUtils.setBugReportStatus(BugReportService.this,
mMetaBugReport, Status.STATUS_AUDIO_PENDING, /* message= */ "");
playNotificationSound();
- startActivity(BugReportActivity.buildAddAudioIntent(this, mMetaBugReport));
+ startActivity(BugReportActivity.buildAddAudioIntent(this, mMetaBugReport.getId()));
} else {
// NOTE: If bugreport is TYPE_AUDIO_FIRST, it will already contain an audio message.
Status status = mConfig.isAutoUpload()
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/FileUtils.java b/tests/BugReportApp/src/com/android/car/bugreport/FileUtils.java
index 96b00d9..8baa97d 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/FileUtils.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/FileUtils.java
@@ -52,23 +52,15 @@
}
/**
- * Creates and returns file directory for storing bug report files before they are zipped into
- * a single file.
- */
- static File createTempDir(Context context, String timestamp) {
- File dir = getTempDir(context, timestamp);
- dir.mkdirs();
- return dir;
- }
-
- /**
* Returns path to the directory for storing bug report files before they are zipped into a
* single file.
*/
static File getTempDir(Context context, String timestamp) {
Preconditions.checkArgument(!context.getUser().isSystem(),
"Must be called from the current user.");
- return new File(context.getDataDir(), TEMP_DIR + "/" + timestamp);
+ File dir = new File(context.getDataDir(), TEMP_DIR + "/" + timestamp);
+ dir.mkdirs();
+ return dir;
}
/**
@@ -96,7 +88,7 @@
+ "-" + lookupCode + "-message." + extension);
}
- private static String extractLookupCode(MetaBugReport bug) {
+ public static String extractLookupCode(MetaBugReport bug) {
Preconditions.checkArgument(bug.getTitle().startsWith("["),
"Invalid bugreport title, doesn't contain lookup code. ");
return bug.getTitle().substring(1, BugReportActivity.LOOKUP_STRING_LENGTH + 1);
@@ -112,7 +104,7 @@
* @return A file.
*/
static File getFileWithSuffix(Context context, String timestamp, String suffix) {
- return new File(createTempDir(context, timestamp), timestamp + suffix);
+ return new File(getTempDir(context, timestamp), timestamp + suffix);
}
/**
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotActivity.java b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotActivity.java
new file mode 100644
index 0000000..1c4c215
--- /dev/null
+++ b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.bugreport;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Activity that starts ScreenshotService to take screenshots.
+ *
+ * <p>This is a simple activity which does not have UI and starts ScreenshotService service only to
+ * take screenshots via a system bar button.
+ */
+public class ScreenshotActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Preconditions.checkState(Config.isBugReportEnabled(), "BugReport is disabled.");
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ startScreenshotService();
+ }
+
+ private void startScreenshotService() {
+ Intent intent = new Intent(this, ScreenshotService.class);
+ startForegroundService(intent);
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+}
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotService.java b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotService.java
new file mode 100644
index 0000000..f2c0048
--- /dev/null
+++ b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotService.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.bugreport;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.car.Car;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Display;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ScreenshotService extends Service {
+ private static final String TAG = ScreenshotService.class.getSimpleName();
+
+ /** Notifications on this channel will silently appear in notification bar. */
+ private static final String NOTIFICATION_CHANNEL_ID = "BUGREPORT_SCREENSHOT_CHANNEL";
+ private static final int SCREENSHOT_NOTIFICATION_ID = 3;
+
+ private final AtomicBoolean mIsTakingScreenshot = new AtomicBoolean(false);
+ private Context mWindowContext;
+ private Car mCar;
+ private ExecutorService mSingleThreadExecutor;
+ private Handler mHandler;
+
+ @Override
+ public void onCreate() {
+ Preconditions.checkState(Config.isBugReportEnabled(), "BugReport is disabled.");
+
+ DisplayManager displayManager = getSystemService(DisplayManager.class);
+ Display primaryDisplay = null;
+ if (displayManager != null) {
+ primaryDisplay = displayManager.getDisplay(DEFAULT_DISPLAY);
+ }
+ mWindowContext = createDisplayContext(primaryDisplay)
+ .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+ mSingleThreadExecutor = Executors.newSingleThreadExecutor();
+ mHandler = new Handler(Looper.myLooper());
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (mIsTakingScreenshot.get()) {
+ Log.w(TAG, "Screenshot is already being taken, ignoring");
+ return START_NOT_STICKY;
+ }
+ mIsTakingScreenshot.set(true);
+ startForeground(SCREENSHOT_NOTIFICATION_ID, buildNotification(),
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
+
+ startScreenshotTask();
+
+ return START_NOT_STICKY;
+ }
+
+ private Notification buildNotification() {
+ NotificationManager manager = getBaseContext().getSystemService(NotificationManager.class);
+
+ if (manager != null) {
+ NotificationChannel serviceChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ getString(R.string.notification_screenshot_channel_name),
+ NotificationManager.IMPORTANCE_LOW);
+ manager.createNotificationChannel(serviceChannel);
+ }
+
+ return new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(getString(R.string.notification_screenshot_message))
+ .setSmallIcon(R.drawable.download_animation)
+ .build();
+ }
+
+ private void startScreenshotTask() {
+ connectToCarService();
+ mSingleThreadExecutor.execute(this::takeScreenshot);
+ }
+
+ private void connectToCarService() {
+ if (mCar == null || !(mCar.isConnected() || mCar.isConnecting())) {
+ mCar = Car.createCar(this);
+ }
+ }
+
+ private void disconnectFromCarService() {
+ if (mCar != null && mCar.isConnected()) {
+ mCar.disconnect();
+ mCar = null;
+ }
+ }
+
+ private void takeScreenshot() {
+ ScreenshotUtils.takeScreenshot(this, mCar);
+ mHandler.post(this::finishScreenshotTask);
+ }
+
+ private void finishScreenshotTask() {
+ Toast.makeText(mWindowContext,
+ getText(R.string.toast_screenshot_finished), Toast.LENGTH_SHORT).show();
+ stopForeground(STOP_FOREGROUND_REMOVE);
+ stopSelf();
+ mIsTakingScreenshot.set(false);
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ disconnectFromCarService();
+ super.onDestroy();
+ }
+}
+
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotUtils.java b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotUtils.java
new file mode 100644
index 0000000..db49f1a
--- /dev/null
+++ b/tests/BugReportApp/src/com/android/car/bugreport/ScreenshotUtils.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.bugreport;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.CarOccupantZoneManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.ScreenCapture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.Set;
+
+
+final class ScreenshotUtils {
+ private static final String TAG = ScreenshotUtils.class.getSimpleName();
+
+ private static final float TITLE_TEXT_SIZE = 30;
+ private static final float TITLE_TEXT_MARGIN = 10;
+ private static final String SCREENSHOT_FILE_EXTENSION = "png";
+ private static final Bitmap.CompressFormat SCREENSHOT_BITMAP_COMPRESS_FORMAT =
+ Bitmap.CompressFormat.PNG;
+ private static final Bitmap.Config SCREENSHOT_BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
+
+ /**
+ * Gets a screenshot directory in the Environment.getExternalStorageDirectory(). Creates if the
+ * directory doesn't exist.
+ */
+ @Nullable
+ public static String getScreenshotDir() {
+ File filesDir = Environment.getExternalStorageDirectory();
+ if (filesDir == null) {
+ Log.e(TAG, "Failed to create a directory, filesDir is null.");
+ return null;
+ }
+
+ String dir = filesDir.getAbsolutePath() + "/screenshots";
+ File storeDirectory = new File(dir);
+ if (!storeDirectory.exists()) {
+ if (!storeDirectory.mkdirs()) {
+ Log.e(TAG, "Failed to create file storage directory.");
+ return null;
+ }
+ }
+
+ return dir;
+ }
+
+ /** Takes screenshots of all displays and stores them to a storage. */
+ public static void takeScreenshot(@NonNull Context context, @Nullable Car car) {
+ Log.i(TAG, "takeScreenshot is started.");
+
+ CarOccupantZoneManager carOccupantZoneManager = null;
+ if (car != null) {
+ carOccupantZoneManager = (CarOccupantZoneManager) car.getCarManager(
+ Car.CAR_OCCUPANT_ZONE_SERVICE);
+ }
+
+ Set<Integer> displayIds = getDisplayIds(context, carOccupantZoneManager);
+ List<Bitmap> images = new ArrayList<>();
+ for (int displayId : displayIds) {
+ Bitmap image = takeScreenshotOfDisplay(displayId);
+ if (image == null) {
+ continue;
+ }
+ image = addTextToImage(image, "Display ID: " + displayId);
+ images.add(image);
+ }
+
+ if (images.size() == 0) {
+ Log.w(TAG, "There is no screenshot taken successfully.");
+ return;
+ }
+
+ Bitmap fullImage = mergeImagesVertically(images);
+
+ storeImage(fullImage, getScreenshotFilename());
+ Log.i(TAG, "takeScreenshot is finished.");
+ }
+
+ /**
+ * Gets all display ids including a cluster display id if possible. It requires a permission
+ * android.car.permission.ACCESS_PRIVATE_DISPLAY_ID to get a cluster display's id.
+ */
+ @NonNull
+ private static Set<Integer> getDisplayIds(@NonNull Context context,
+ @Nullable CarOccupantZoneManager carOccupantZoneManager) {
+ Set<Integer> displayIds = new HashSet<>();
+
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ if (displayManager == null) {
+ Log.e(TAG, "Failed to get DisplayManager.");
+ return displayIds;
+ }
+
+ Display[] displays = displayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ for (Display display : displays) {
+ displayIds.add(display.getDisplayId());
+ }
+
+ OptionalInt clusterDisplayId = getClusterDisplayId(context, carOccupantZoneManager);
+ if (clusterDisplayId.isPresent()) {
+ displayIds.add(clusterDisplayId.getAsInt());
+ }
+
+ Log.d(TAG, "Display ids : " + displayIds);
+
+ return displayIds;
+ }
+
+ /** Gets cluster display id if possible. Or returns an empty instance. */
+ private static OptionalInt getClusterDisplayId(@NonNull Context context,
+ @Nullable CarOccupantZoneManager carOccupantZoneManager) {
+ if (context.checkSelfPermission(Car.ACCESS_PRIVATE_DISPLAY_ID)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "android.car.permission.ACCESS_PRIVATE_DISPLAY_ID is not granted.");
+ return OptionalInt.empty();
+ }
+ if (carOccupantZoneManager == null) {
+ Log.w(TAG, "CarOccupantZoneManager is null.");
+ return OptionalInt.empty();
+ }
+
+ int clusterDisplayId = carOccupantZoneManager.getDisplayIdForDriver(
+ CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
+ return OptionalInt.of(clusterDisplayId);
+ }
+
+ /** Gets filename of screenshot based on the current time. */
+ private static String getScreenshotFilename() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss").withZone(
+ ZoneId.systemDefault());
+ String nowInDateTimeFormat = formatter.format(Instant.now());
+ return "extra_screenshot_" + nowInDateTimeFormat + "." + SCREENSHOT_FILE_EXTENSION;
+ }
+
+ /** Adds a text to the top of the image. */
+ @NonNull
+ private static Bitmap addTextToImage(@NonNull Bitmap image, String text) {
+ Paint paint = new Paint();
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(TITLE_TEXT_SIZE);
+ Rect textBounds = new Rect();
+ paint.getTextBounds(text, 0, text.length(), textBounds);
+
+ float extraHeight = textBounds.height() + TITLE_TEXT_MARGIN * 2;
+
+ Bitmap imageWithTitle = Bitmap.createBitmap(image.getWidth(),
+ image.getHeight() + (int) extraHeight, image.getConfig());
+ Canvas canvas = new Canvas(imageWithTitle);
+ canvas.drawColor(Color.WHITE);
+ canvas.drawText(text, TITLE_TEXT_MARGIN,
+ extraHeight - TITLE_TEXT_MARGIN - textBounds.bottom, paint);
+ canvas.drawBitmap(image, 0, extraHeight, null);
+ return imageWithTitle;
+ }
+
+ @NonNull
+ private static Bitmap mergeImagesVertically(@NonNull List<Bitmap> images) {
+ int width = images.stream().mapToInt(Bitmap::getWidth).max().orElse(0);
+ int height = images.stream().mapToInt(Bitmap::getHeight).sum();
+
+ Bitmap mergedImage = Bitmap.createBitmap(width, height, SCREENSHOT_BITMAP_CONFIG);
+ Canvas canvas = new Canvas(mergedImage);
+ canvas.drawColor(Color.WHITE);
+
+ float curHeight = 0;
+ for (Bitmap image : images) {
+ canvas.drawBitmap(image, 0f, curHeight, null);
+ curHeight += image.getHeight();
+ }
+ return mergedImage;
+ }
+
+ /** Stores an image with the given fileName. */
+ private static void storeImage(@NonNull Bitmap image, String fileName) {
+ String screenshotDir = getScreenshotDir();
+ if (screenshotDir == null) {
+ return;
+ }
+
+ String filePath = screenshotDir + "/" + fileName;
+ try {
+ FileOutputStream fos = new FileOutputStream(filePath);
+ image.compress(SCREENSHOT_BITMAP_COMPRESS_FORMAT, 100, fos);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "File " + filePath + " not found to store screenshot.", e);
+ return;
+ }
+
+ Log.i(TAG, "Screenshot is stored in " + filePath);
+ }
+
+ /** Takes screenshots of the certain display. Returns null if it fails to take a screenshot. */
+ @Nullable
+ private static Bitmap takeScreenshotOfDisplay(int displayId) {
+ Log.d(TAG, "Take screenshot of display " + displayId);
+ IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
+
+ ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>().build();
+ ScreenCapture.SynchronousScreenCaptureListener syncScreenCaptureListener =
+ ScreenCapture.createSyncCaptureListener();
+
+ try {
+ windowManager.captureDisplay(displayId, captureArgs, syncScreenCaptureListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to take screenshot", e);
+ return null;
+ }
+
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ syncScreenCaptureListener.getBuffer();
+ if (screenshotBuffer == null) {
+ return null;
+ }
+ return screenshotBuffer.asBitmap().copy(SCREENSHOT_BITMAP_CONFIG, /* isMutable= */ true);
+ }
+}
diff --git a/tests/BugReportApp/src/com/android/car/bugreport/SimpleUploaderAsyncTask.java b/tests/BugReportApp/src/com/android/car/bugreport/SimpleUploaderAsyncTask.java
index 46ab0af..de6a639 100644
--- a/tests/BugReportApp/src/com/android/car/bugreport/SimpleUploaderAsyncTask.java
+++ b/tests/BugReportApp/src/com/android/car/bugreport/SimpleUploaderAsyncTask.java
@@ -142,10 +142,10 @@
Storage storage = new Storage.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("Bugreportupload/1.0").build();
- File tmpBugReportFile = zipBugReportFiles(bugReport);
+ File zipFileToUpload = zipBugReportFiles(bugReport);
String uploadName = bugReport.getBugReportFileName();
- Log.d(TAG, "Uploading file " + tmpBugReportFile + " as " + uploadName);
- try (FileInputStream inputStream = new FileInputStream(tmpBugReportFile)) {
+ Log.d(TAG, "Uploading file " + zipFileToUpload + " as " + uploadName);
+ try (FileInputStream inputStream = new FileInputStream(zipFileToUpload)) {
StorageObject object = uploadSimple(storage, bugReport, uploadName, inputStream);
Log.v(TAG, "finished uploading object " + object.getName());
File pendingDir = FileUtils.getPendingDir(mContext);
@@ -159,10 +159,21 @@
deleteFileQuietly(new File(pendingDir, bugReport.getBugReportFileName()));
}
} finally {
- Log.v(TAG, "Deleting file " + tmpBugReportFile);
// No need to throw exception even if it fails to delete the file, as the task
// shouldn't retry the upload again.
- deleteFileQuietly(tmpBugReportFile);
+ Log.v(TAG, "Deleting file " + zipFileToUpload);
+ deleteFileQuietly(zipFileToUpload);
+
+ // Deletes unlinked wav files from MetaBugReport because of re-recording.
+ String lookupCode = FileUtils.extractLookupCode(bugReport).toLowerCase();
+ File pendingDir = FileUtils.getPendingDir(mContext);
+ File[] files = pendingDir.listFiles();
+ for (File file : files) {
+ if (file.getName().toLowerCase().contains(lookupCode)) {
+ Log.v(TAG, "Deleting file " + file.getCanonicalPath());
+ deleteFileQuietly(file);
+ }
+ }
}
}
diff --git a/tests/BugReportApp/tests/Android.bp b/tests/BugReportApp/tests/Android.bp
index aab6c13..41ccdd8 100644
--- a/tests/BugReportApp/tests/Android.bp
+++ b/tests/BugReportApp/tests/Android.bp
@@ -30,6 +30,7 @@
platform_apis: true,
libs: [
+ "android.car",
"android.test.base",
"android.test.mock",
"android.test.runner",
diff --git a/tests/BugReportApp/tests/src/com/android/car/bugreport/ScreenshotUtilsTest.java b/tests/BugReportApp/tests/src/com/android/car/bugreport/ScreenshotUtilsTest.java
new file mode 100644
index 0000000..9168601
--- /dev/null
+++ b/tests/BugReportApp/tests/src/com/android/car/bugreport/ScreenshotUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.bugreport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.Car;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class ScreenshotUtilsTest {
+ private static final String TAG = ScreenshotUtils.class.getSimpleName();
+
+ private Context mContext;
+ private Car mCar;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ mCar = Car.createCar(mContext);
+ deleteScreenshotDir();
+ }
+
+ @Test
+ public void test_getScreenshotDir_returnsScreenshotDir() throws Exception {
+ String screenshotDir = ScreenshotUtils.getScreenshotDir();
+
+ assertThat(screenshotDir).endsWith("screenshots");
+ assertThat(new File(screenshotDir).exists()).isTrue();
+ }
+
+ @Test
+ public void test_takeScreenshot_takesAndStoresScreenshot() throws Exception {
+ ScreenshotUtils.takeScreenshot(mContext, mCar);
+
+ File screenshotDir = new File(ScreenshotUtils.getScreenshotDir());
+
+
+ assertThat(screenshotDir.exists()).isTrue();
+ String[] screenshots = screenshotDir.list();
+ assertThat(screenshots).hasLength(1);
+ assertThat(screenshots[0]).matches(
+ "extra_screenshot_\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}.png");
+ }
+
+ private void deleteScreenshotDir() {
+ boolean result = true;
+ String screenshotDirPath = ScreenshotUtils.getScreenshotDir();
+ File screenshotDir = new File(screenshotDirPath);
+ if (screenshotDir.exists() && screenshotDir.isDirectory()) {
+ String[] children = screenshotDir.list();
+ for (String child : children) {
+ result &= new File(screenshotDir, child).delete();
+ }
+ }
+ result &= screenshotDir.delete();
+ Log.d(TAG, "deleteScreenshotDir result = " + result);
+ }
+}
diff --git a/tests/CarEvsCameraPreviewApp/res/values/config.xml b/tests/CarEvsCameraPreviewApp/res/values/config.xml
index 85eb0b1..94e9ee5 100644
--- a/tests/CarEvsCameraPreviewApp/res/values/config.xml
+++ b/tests/CarEvsCameraPreviewApp/res/values/config.xml
@@ -20,4 +20,7 @@
<!-- In-plane rotation angle of the rearview camera device in degree -->
<integer name="config_evsRearviewCameraInPlaneRotationAngle">0</integer>
+
+ <!-- CarEvsService type this application is going to use. -->
+ <string name="config_evsCameraType">REARVIEW</string>
</resources>
diff --git a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraActivity.java b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraActivity.java
index 4c3eaa1..7061e71 100644
--- a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraActivity.java
+++ b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraActivity.java
@@ -38,7 +38,11 @@
try {
CarEvsManager evsManager = (CarEvsManager) car.getCarManager(
Car.CAR_EVS_SERVICE);
- if (evsManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW) != ERROR_NONE) {
+ String config = getApplicationContext().getResources()
+ .getString(R.string.config_evsCameraType);
+ int type = config == null ?
+ CarEvsManager.SERVICE_TYPE_REARVIEW : getServiceType(config);
+ if (evsManager.startActivity(type) != ERROR_NONE) {
Log.e(TAG, "Failed to start a camera preview activity");
}
} finally {
@@ -49,6 +53,23 @@
private Car mCar;
+ static int getServiceType(String rawString) {
+ switch (rawString) {
+ case "REARVIEW": return CarEvsManager.SERVICE_TYPE_REARVIEW;
+ case "SURROUNDVIEW": return CarEvsManager.SERVICE_TYPE_SURROUNDVIEW;
+ case "FRONTVIEW": return CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+ case "LEFTVIEW": return CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+ case "RIGHTVIEW": return CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+ case "DRIVERVIEW": return CarEvsManager.SERVICE_TYPE_DRIVERVIEW;
+ case "FRONT_PASSENGERSVIEW": return CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW;
+ case "REAR_PASSENGERSVIEW": return CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW;
+ case "USER_DEFINEDVIEW": return CarEvsManager.SERVICE_TYPE_USER_DEFINED;
+ default:
+ Log.w(TAG, "Unknown service type: " + rawString);
+ return CarEvsManager.SERVICE_TYPE_REARVIEW;
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
index 03cb5dd..f9b93f4 100644
--- a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
+++ b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
@@ -73,6 +73,15 @@
private final static int STREAM_STATE_INVISIBLE = 2;
private final static int STREAM_STATE_LOST = 3;
+ private final static float DEFAULT_1X1_POSITION[][] = {
+ {
+ -1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f,
+ },
+ };
+
private static String streamStateToString(int state) {
switch (state) {
case STREAM_STATE_STOPPED:
@@ -126,6 +135,7 @@
private IBinder mSessionToken;
private boolean mUseSystemWindow;
+ private int mServiceType;
/** Callback to listen to EVS stream */
private final CarEvsManager.CarEvsStreamCallback mStreamHandler =
@@ -260,8 +270,12 @@
Car.createCar(getApplicationContext(), /* handler = */ null,
Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
- mEvsView = CarEvsGLSurfaceView.create(getApplication(), this, getApplicationContext()
- .getResources().getInteger(R.integer.config_evsRearviewCameraInPlaneRotationAngle));
+ // Packaging parameters to create CarEvsGLSurfaceView.
+ ArrayList callbacks = new ArrayList<>(1);
+ callbacks.add(CarEvsManager.SERVICE_TYPE_REARVIEW, this);
+ mEvsView = CarEvsGLSurfaceView.create(getApplication(), callbacks, getApplicationContext()
+ .getResources().getInteger(R.integer.config_evsRearviewCameraInPlaneRotationAngle),
+ DEFAULT_1X1_POSITION);
mRootView = (ViewGroup) LayoutInflater.from(this).inflate(
R.layout.evs_preview_activity, /* root= */ null);
mPreviewContainer = mRootView.findViewById(R.id.evs_preview_container);
@@ -274,7 +288,7 @@
mPreviewContainer.addView(mEvsView, 0);
View closeButton = mRootView.findViewById(R.id.close_button);
if (closeButton != null) {
- closeButton.setOnClickListener(v -> finish());
+ closeButton.setOnClickListener(v -> handleCloseButtonTriggered());
}
int width = WindowManager.LayoutParams.MATCH_PARENT;
@@ -310,10 +324,14 @@
Bundle extras = intent.getExtras();
if (extras == null) {
mSessionToken = null;
+ mServiceType = CarEvsManager.SERVICE_TYPE_REARVIEW;
+ mUseSystemWindow = false;
return;
}
+
mSessionToken = extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN);
mUseSystemWindow = mSessionToken != null;
+ mServiceType = extras.getShort(Integer.toString(CarEvsManager.SERVICE_TYPE_REARVIEW));
}
@Override
@@ -330,10 +348,10 @@
protected void onStop() {
Log.d(TAG, "onStop");
try {
- if (mUseSystemWindow) {
+ if (mUseSystemWindow && mEvsView.getWindowVisibility() == View.VISIBLE) {
// When a new activity is launched, this activity will become the background
// activity and, however, likely still visible to the users if it is using the
- // system window. Therefore, we should not transition to the INVISIBLE state.
+ // system window. Therefore, we should not transition to the STOPPED state.
//
// Similarly, this activity continues previewing the camera when the user triggers
// the home button. If the users want to manually close the preview window, they
@@ -342,7 +360,7 @@
}
synchronized (mLock) {
- handleVideoStreamLocked(STREAM_STATE_INVISIBLE);
+ handleVideoStreamLocked(STREAM_STATE_STOPPED);
}
} finally {
super.onStop();
@@ -400,8 +418,8 @@
case STREAM_STATE_VISIBLE:
// Starts a video stream
if (mEvsManager != null) {
- int result = mEvsManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
- mSessionToken, mCallbackExecutor, mStreamHandler);
+ int result = mEvsManager.startVideoStream(mServiceType, mSessionToken,
+ mCallbackExecutor, mStreamHandler);
if (result != ERROR_NONE) {
Log.e(TAG, "Failed to start a video stream, error = " + result);
} else {
@@ -476,4 +494,12 @@
Log.w(TAG, "CarEvsService is not available.");
}
}
+
+ private void handleCloseButtonTriggered() {
+ // It is possible that we've been stopped but a video stream is still active.
+ synchronized (mLock) {
+ handleVideoStreamLocked(STREAM_STATE_STOPPED);
+ }
+ finish();
+ }
}
diff --git a/tests/CarEvsMultiCameraPreviewApp/Android.bp b/tests/CarEvsMultiCameraPreviewApp/Android.bp
new file mode 100644
index 0000000..8dfb32e
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/Android.bp
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2023 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"],
+}
+
+android_app {
+ name: "CarEvsMultiCameraPreviewApp",
+
+ owner: "google",
+
+ srcs: ["src/**/*.java"],
+
+ resource_dirs: ["res"],
+
+ // registerReceiverForAllUsers() is a hidden api.
+ platform_apis: true,
+
+ certificate: "platform",
+
+ optimize: {
+ enabled: false,
+ },
+
+ // Disable dexpreopt and verify_uses_libraries check as the app contains
+ // no Java code to be dexpreopted.
+ enforce_uses_libs:false,
+ dex_preopt: {
+ enabled: false,
+ },
+
+ libs: [
+ "android.car",
+ "android.car-system-stubs",
+ ],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "car-evs-helper-lib",
+ ],
+
+ // To make this app be able to re-installed
+ use_embedded_native_libs: true,
+ jni_libs: ["libcarevsglrenderer_jni"],
+}
diff --git a/tests/CarEvsMultiCameraPreviewApp/AndroidManifest.xml b/tests/CarEvsMultiCameraPreviewApp/AndroidManifest.xml
new file mode 100644
index 0000000..b334725
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.car.evs.multi">
+
+ <!-- Allows the app to use EVS service and monitor its status -->
+ <uses-permission android:name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY" />
+ <uses-permission android:name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY" />
+ <uses-permission android:name="android.car.permission.USE_CAR_EVS_CAMERA" />
+ <uses-permission android:name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
+
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+ <!-- for registerReceiverForAllUsers() -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/rearview"
+ android:hardwareAccelerated="true"
+ android:extractNativeLibs="false">
+
+ <activity android:name=".CarEvsMultiCameraPreviewActivity"
+ android:exported="true"
+ android:label="@string/app_name"
+ android:launchMode="singleTask"
+ android:resizeableActivity="false"
+ android:showForAllUsers="true"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:turnScreenOn="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="distractionOptimized" android:value="true"/>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/CarEvsMultiCameraPreviewApp/OWNERS b/tests/CarEvsMultiCameraPreviewApp/OWNERS
new file mode 100644
index 0000000..88b80c5
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/OWNERS
@@ -0,0 +1,3 @@
+# Project owners
+ankitarora@google.com
+changyeon@google.com
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/drawable-hdpi/rearview.png b/tests/CarEvsMultiCameraPreviewApp/res/drawable-hdpi/rearview.png
new file mode 100644
index 0000000..a5b640c
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/drawable-hdpi/rearview.png
Binary files differ
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/drawable-mdpi/rearview.png b/tests/CarEvsMultiCameraPreviewApp/res/drawable-mdpi/rearview.png
new file mode 100644
index 0000000..8915f07
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/drawable-mdpi/rearview.png
Binary files differ
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/drawable-xhdpi/rearview.png b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xhdpi/rearview.png
new file mode 100644
index 0000000..080182b
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xhdpi/rearview.png
Binary files differ
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxhdpi/rearview.png b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxhdpi/rearview.png
new file mode 100644
index 0000000..2ee66ba
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxhdpi/rearview.png
Binary files differ
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxxhdpi/rearview.png b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxxhdpi/rearview.png
new file mode 100644
index 0000000..5f124c5
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/drawable-xxxhdpi/rearview.png
Binary files differ
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/layout/evs_preview_activity.xml b/tests/CarEvsMultiCameraPreviewApp/res/layout/evs_preview_activity.xml
new file mode 100644
index 0000000..ed9de75
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/layout/evs_preview_activity.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/evs_preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="15"
+ android:background="@android:color/transparent"
+ android:orientation="vertical">
+
+ <ViewSwitcher android:id="@+id/evs_preview_switcher"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="12">
+ <LinearLayout android:id="@+id/evs_switcher_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="12"
+ android:gravity="center"
+ android:orientation="vertical"/>
+ </ViewSwitcher>
+
+ <LinearLayout android:id="@+id/evs_checkboxes_container0"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0dp">
+
+ <TextView android:id="@+id/checkbox_label"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:background="@color/textview_background"
+ android:textSize="@dimen/checkbox_text_size"
+ android:text="@string/textview_text_car0"/>
+
+ <CheckBox android:id="@+id/checkbox_rearview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_rearview"/>
+
+ <CheckBox android:id="@+id/checkbox_frontview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_frontview"/>
+
+ <CheckBox android:id="@+id/checkbox_leftview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_leftview"/>
+
+ <CheckBox android:id="@+id/checkbox_rightview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_rightview"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/evs_checkboxes_container1"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0dp">
+
+ <TextView android:id="@+id/checkbox1_label"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:background="@color/textview_background"
+ android:textSize="@dimen/checkbox_text_size"
+ android:text="@string/textview_text_car1"/>
+
+ <CheckBox android:id="@+id/checkbox1_rearview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_rearview"/>
+
+ <CheckBox android:id="@+id/checkbox1_frontview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_frontview"/>
+
+ <CheckBox android:id="@+id/checkbox1_leftview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_leftview"/>
+
+ <CheckBox android:id="@+id/checkbox1_rightview"
+ android:textSize="@dimen/checkbox_text_size"
+ android:textColor="@color/checkbox_text"
+ android:background="@color/checkbox_background"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_text_rightview"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/evs_buttons_container"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ style="?android:attr/buttonBarStyle">
+
+ <Button android:id="@+id/apply_button"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/apply_button_text"
+ android:textColor="@color/button_text"
+ android:background="@color/button_background"
+ android:textSize="@dimen/close_button_text_size"
+ style="?android:attr/buttonBarButtonStyle"/>
+
+ <Button android:id="@+id/close_button"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/close_button_text"
+ android:textColor="@color/button_text"
+ android:background="@color/button_background"
+ android:textSize="@dimen/close_button_text_size"
+ style="?android:attr/buttonBarButtonStyle"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/colors.xml
similarity index 67%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to tests/CarEvsMultiCameraPreviewApp/res/values/colors.xml
index 8ad0860..b1a56d0 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/colors.xml
@@ -15,5 +15,9 @@
~ limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
+ <color name="button_text">@*android:color/car_body1_light</color>
+ <color name="button_background">@*android:color/car_card_dark</color>
+ <color name="checkbox_text">@*android:color/car_body1_light</color>
+ <color name="checkbox_background">@*android:color/car_card_dark</color>
+ <color name="textview_background">@*android:color/car_card_dark</color>
</resources>
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/values/config.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/config.xml
new file mode 100644
index 0000000..af1e4b4
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/config.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<resources>
+ <!-- Shade of the background behind the camera window. 1.0 for fully opaque, 0.0 for fully
+ transparent. -->
+ <item name="config_cameraBackgroundScrim" format="float" type="dimen">0.7</item>
+
+ <!-- In-plane rotation angle of the rearview camera device in degree -->
+ <integer name="config_evsRearviewCameraInPlaneRotationAngle">0</integer>
+
+ <!-- CarEvsService type this application is going to use. -->
+ <string name="config_evsCameraType">REARVIEW</string>
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/dimens.xml
similarity index 68%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to tests/CarEvsMultiCameraPreviewApp/res/values/dimens.xml
index 8ad0860..59cddb3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/dimens.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,6 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
+<resources>
+ <!-- dimensions for evs camera preview in the system window -->
+ <dimen name="camera_preview_width">800dp</dimen>
+ <dimen name="camera_preview_height">600dp</dimen>
+
+ <dimen name="close_button_text_size">30sp</dimen>
+ <dimen name="checkbox_text_size">25sp</dimen>
</resources>
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/values/overlayable.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/overlayable.xml
new file mode 100644
index 0000000..02d3f8f
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/overlayable.xml
@@ -0,0 +1,34 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Copyright (C) 2023 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.-->
+<!--
+THIS FILE WAS AUTO GENERATED, DO NOT EDIT MANUALLY.
+REGENERATE USING packages/apps/Car/tests/tools/rro/generate-overlayable.py
+-->
+<resources>
+ <overlayable name="CarEvsMultiCameraPreviewApp">
+ <policy type="system|product|signature">
+ <item type="color" name="button_background"/>
+ <item type="color" name="button_text"/>
+ <item type="dimen" name="camera_preview_height"/>
+ <item type="dimen" name="camera_preview_width"/>
+ <item type="dimen" name="close_button_text_size"/>
+ <item type="dimen" name="config_cameraBackgroundScrim"/>
+ <item type="id" name="close_button"/>
+ <item type="id" name="evs_preview_switcher"/>
+ <item type="layout" name="evs_preview_activity"/>
+ <item type="string" name="app_name"/>
+ <item type="string" name="close_button_text"/>
+ <item type="style" name="Theme.Transparent"/>
+ <item type="integer" name="config_evsRearviewCameraInPlaneRotationAngle"/>
+ </policy>
+ </overlayable>
+</resources>
diff --git a/tests/CarEvsMultiCameraPreviewApp/res/values/strings.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/strings.xml
new file mode 100644
index 0000000..4c38904
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 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.
+ -->
+
+<resources>
+ <string name="app_name">EvsMultiCameraPreview</string>
+ <string type="string" name="close_button_text">Close</string>
+ <string type="string" name="apply_button_text">Apply</string>
+ <string type="string" name="textview_text_car0">Car0</string>
+ <string type="string" name="textview_text_car1">Car1</string>
+ <string type="string" name="checkbox_text_rearview">REARVIEW</string>
+ <string type="string" name="checkbox_text_frontview">FRONTVIEW</string>
+ <string type="string" name="checkbox_text_leftview">LEFTVIEW</string>
+ <string type="string" name="checkbox_text_rightview">RIGHTVIEW</string>
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/tests/CarEvsMultiCameraPreviewApp/res/values/themes.xml
similarity index 62%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to tests/CarEvsMultiCameraPreviewApp/res/values/themes.xml
index 8ad0860..5bcefcf 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/tests/CarEvsMultiCameraPreviewApp/res/values/themes.xml
@@ -14,6 +14,12 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
+<resources>
+ <style name="Theme.Transparent" parent="android:Theme">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
</resources>
diff --git a/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsCameraClient.java b/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsCameraClient.java
new file mode 100644
index 0000000..9da6f7d
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsCameraClient.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.evs.multi;
+
+import static android.car.evs.CarEvsManager.ERROR_NONE;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
+import android.car.evs.CarEvsBufferDescriptor;
+import android.car.evs.CarEvsManager;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.GuardedBy;
+
+import com.android.car.internal.evs.CarEvsGLSurfaceView;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * This class represents a single Car client and manages ICarEvsStreamCallback and
+ * CarEvsGLSurface.BufferCallback objects to run camera preview.
+ */
+final class CarEvsCameraClient {
+
+ private static final String TAG = CarEvsCameraClient.class.getSimpleName();
+
+ /**
+ * Defines internal states.
+ */
+ private final static int STREAM_STATE_STOPPED = 0;
+ private final static int STREAM_STATE_VISIBLE = 1;
+ private final static int STREAM_STATE_INVISIBLE = 2;
+ private final static int STREAM_STATE_LOST = 3;
+
+ /**
+ * CarEvsBufferDescriptor id contains its service type in 8-MSB of 32-bit word and
+ * a EVS frame buffer id in the rest of bits.
+ */
+ private static final int BUFFER_ID_BITDEPTH = 24;
+
+ /* Use a ReentrantLock to get waiters. */
+ private final ReentrantLock mLock = new ReentrantLock();
+
+ /** CarEvsStreamCallback implementation. */
+ private final StreamHandler mStreamHandler = new StreamHandler();
+
+ /** Tells whether or not a video stream is running */
+ @GuardedBy("mLock")
+ private int mStreamState = STREAM_STATE_STOPPED;
+
+ /**
+ * Buffer queue to store received frames per service type. This member will be accessed by
+ * Binder thread (ICarEvsStreamCallback) and GL thread (CarEvsGLSurfaceView.BufferCallback).
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ArrayList<CarEvsBufferDescriptor>> mBufferQueue = new SparseArray<>();
+
+ /** Callback executor */
+ private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
+
+ /** Service types currently we are running. */
+ @GuardedBy("mLock")
+ private final ArraySet<Integer> mNextServiceTypes = new ArraySet<>();
+
+ /** List of CarEvsGLSurface.BufferCallback implementations. */
+ @GuardedBy("mLock")
+ private final ArrayList<CarEvsGLSurfaceView.BufferCallback> mSurfaceBufferHandlers =
+ new ArrayList<>();
+
+ /** CarService status listener. */
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ mLock.lock();
+ try {
+ mCar = ready ? car : null;
+ mEvsManager = ready ? (CarEvsManager) car.getCarManager(Car.CAR_EVS_SERVICE) : null;
+ if (ready) {
+ return;
+ }
+
+ handleVideoStreamLocked(STREAM_STATE_STOPPED);
+ } catch (CarNotConnectedException err) {
+ Log.e(TAG, "Failed to connect to the Car Service");
+ } finally {
+ mLock.unlock();
+ }
+ };
+
+ /** Car instance to use. */
+ private Car mCar;
+
+ /** CarEvsManager to use. */
+ private CarEvsManager mEvsManager;
+
+ final class StreamHandler implements CarEvsManager.CarEvsStreamCallback {
+ @Override
+ public void onStreamEvent(int event) {
+ // TOOD: handle stream events.
+ Log.i(TAG, "Client " + this + " received " + event);
+ }
+
+ @Override
+ public void onNewFrame(CarEvsBufferDescriptor desc) {
+ mLock.lock();
+ try {
+ @CarEvsServiceType int type = getServiceType(desc.getId());
+ ArrayList bufferQueue = mBufferQueue.get(type);
+ if (bufferQueue == null) {
+ return;
+ }
+
+ bufferQueue.add(desc);
+ } finally {
+ mLock.unlock();
+ }
+ }
+ }
+
+ final class SurfaceViewBufferHandler implements CarEvsGLSurfaceView.BufferCallback {
+ private final @CarEvsServiceType int mType;
+
+ SurfaceViewBufferHandler(@CarEvsServiceType int type) {
+ mType = type;
+ }
+
+ @Override
+ public CarEvsBufferDescriptor onBufferRequested() {
+ try {
+ if (!mLock.tryLock(/* timeout= */ 100, TimeUnit.MILLISECONDS)) {
+ Log.d(TAG, "Timer for new framebuffer expired.");
+ return null;
+ }
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Timer for new framebuffer is interrupted.");
+ return null;
+ }
+
+ try {
+ ArrayList<CarEvsBufferDescriptor> buffers = mBufferQueue.get(mType);
+ if (buffers == null || buffers.isEmpty()) {
+ Log.d(TAG, "No buffer is available for type=" + mType);
+ return null;
+ }
+
+ // The renderer refreshes the screen faster than the camera frame rate so it's okay
+ // to return the first buffer in the queue always.
+ return buffers.remove(0);
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ @Override
+ public void onBufferProcessed(CarEvsBufferDescriptor buffer) {
+ doneWithBuffer(buffer);
+ }
+ }
+
+ CarEvsCameraClient(CarEvsMultiCameraPreviewActivity activity) {
+ this(activity, /* serviceTypes = */ null);
+ }
+
+ CarEvsCameraClient(CarEvsMultiCameraPreviewActivity activity, ArraySet<Integer> serviceTypes) {
+ if (serviceTypes != null) {
+ mNextServiceTypes.addAll(serviceTypes);
+ }
+
+ Car.createCar(activity.getApplicationContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
+
+ }
+
+ /**
+ * Updates CarEvsService service types that this client instance will use.
+ *
+ * @param types A set of CarEvsManager.SERVICE_TYPE_* constants.
+ * @return false if a given set is identical to what we have.
+ * true otherwise.
+ */
+ boolean updateServiceTypes(ArraySet<Integer> types) {
+ mLock.lock();
+ try {
+ if (mNextServiceTypes.equals(types)) {
+ return false;
+ }
+
+ mNextServiceTypes.clear();
+ mNextServiceTypes.addAll(types);
+ return true;
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Starts video streams for given service types.
+ *
+ * @param types A set of CarEvsManager.SERVICE_TYPE_* this client instance will run.
+ * @return {@code ArraySet<Integer>} that contains successfully started service types.
+ */
+ ArraySet<Integer> startVideoStream(ArraySet<Integer> types) {
+ updateServiceTypes(types);
+ return startVideoStream();
+ }
+
+ /**
+ * Requests to start video streams of service types.
+ *
+ * @return {@code ArraySet<Integer>} that contains successfully started service types.
+ */
+ ArraySet<Integer> startVideoStream() {
+ ArraySet<Integer> started = new ArraySet<>();
+ mLock.lock();
+ try {
+ if (mNextServiceTypes.isEmpty()) {
+ return started;
+ }
+
+ for (var type : mNextServiceTypes) {
+ if (!mBufferQueue.contains(type)) {
+ mBufferQueue.put(type, new ArrayList<>());
+ }
+
+ int res = mEvsManager.startVideoStream(type, /* token= */ null, mCallbackExecutor,
+ mStreamHandler);
+ if (res != ERROR_NONE) {
+ Log.w(TAG, "Failed to start a video for type=" + type + ", error=" + res);
+ continue;
+ }
+
+ mSurfaceBufferHandlers.add(new SurfaceViewBufferHandler(type));
+ started.add(type);
+ }
+ } finally {
+ mLock.unlock();
+ }
+
+ return started;
+ }
+
+ /**
+ * Stops all active video streams managed by our CarEvsManager instance.
+ */
+ void stopVideoStream() {
+ mLock.lock();
+ try {
+ mEvsManager.stopVideoStream();
+ mSurfaceBufferHandlers.clear();
+
+ for (int i = 0; i < mBufferQueue.size(); i++) {
+ if (mBufferQueue.get(i) == null) {
+ Log.w(TAG, "No buffer queue exists for type=" + i);
+ continue;
+ }
+
+ mBufferQueue.get(i).clear();
+ }
+ mBufferQueue.clear();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Stops video streams and release other resources.
+ */
+ void release() {
+ mLock.lock();
+ try {
+ if (mEvsManager != null) {
+ handleVideoStreamLocked(STREAM_STATE_STOPPED);
+ mEvsManager.clearStatusListener();
+ }
+
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Returns a list of CarEvsGLSurfacePreview.BufferCallback instances.
+ *
+ * @return {@code ArrayList<CarEvsGLSurfaceView.BufferCallback>} object. This wouldn't
+ * be null or empty.
+ */
+ ArrayList<CarEvsGLSurfaceView.BufferCallback> getBufferCallbacks() {
+ mLock.lock();
+ try {
+ return mSurfaceBufferHandlers;
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void doneWithBuffer(CarEvsBufferDescriptor buffer) {
+ mLock.lock();
+ try {
+ doneWithBufferLocked(buffer);
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void doneWithBufferLocked(CarEvsBufferDescriptor buffer) {
+ try {
+ mEvsManager.returnFrameBuffer(buffer);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to return a buffer: " + Log.getStackTraceString(e));
+ }
+ }
+
+ void handleVideoStream(int newState) {
+ mLock.lock();
+ try {
+ handleVideoStreamLocked(newState);
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void handleVideoStreamLocked(int newState) {
+ Log.d(TAG, "Requested: " + streamStateToString(mStreamState) + " -> " +
+ streamStateToString(newState));
+ if (newState == mStreamState) {
+ // Safely ignore a request of transitioning to the current state.
+ return;
+ }
+
+ switch (newState) {
+ case STREAM_STATE_STOPPED:
+ stopVideoStream();
+ break;
+
+ case STREAM_STATE_VISIBLE:
+ // Starts a video stream
+ startVideoStream();
+ break;
+
+ case STREAM_STATE_INVISIBLE:
+ break;
+
+ case STREAM_STATE_LOST:
+ break;
+
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ mStreamState = newState;
+ Log.d(TAG, "Completed: " + streamStateToString(mStreamState));
+ }
+
+ private static @CarEvsServiceType int getServiceType(int descId) {
+ return descId >> BUFFER_ID_BITDEPTH;
+ }
+
+ private static String streamStateToString(int state) {
+ switch (state) {
+ case STREAM_STATE_STOPPED:
+ return "STOPPED";
+
+ case STREAM_STATE_VISIBLE:
+ return "VISIBLE";
+
+ case STREAM_STATE_INVISIBLE:
+ return "INVISIBLE";
+
+ case STREAM_STATE_LOST:
+ return "LOST";
+
+ default:
+ return "UNKNOWN: " + state;
+ }
+ }
+}
diff --git a/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsMultiCameraPreviewActivity.java b/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsMultiCameraPreviewActivity.java
new file mode 100644
index 0000000..4c7b2f1
--- /dev/null
+++ b/tests/CarEvsMultiCameraPreviewApp/src/com/google/android/car/evs/multi/CarEvsMultiCameraPreviewActivity.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.evs.multi;
+
+import static android.car.evs.CarEvsManager.ERROR_NONE;
+import static android.hardware.display.DisplayManager.DisplayListener;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.evs.CarEvsManager;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import android.widget.ViewSwitcher;
+
+import androidx.annotation.GuardedBy;
+
+import com.android.car.internal.evs.CarEvsGLSurfaceView;
+import com.android.car.internal.evs.GLES20CarEvsBufferRenderer;
+
+import java.lang.CharSequence;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class CarEvsMultiCameraPreviewActivity extends Activity {
+
+ private static final String TAG = CarEvsMultiCameraPreviewActivity.class.getSimpleName();
+
+ /**
+ * ActivityManagerService encodes the reason for a request to close system dialogs with this
+ * key.
+ */
+ private final static String EXTRA_DIALOG_CLOSE_REASON = "reason";
+ /** This string literal is from com.android.systemui.car.systembar.CarSystemBarButton class. */
+ private final static String DIALOG_CLOSE_REASON_CAR_SYSTEMBAR_BUTTON = "carsystembarbutton";
+ /** This string literal is from com.android.server.policy.PhoneWindowManager class. */
+ private final static String DIALOG_CLOSE_REASON_HOME_KEY = "homekey";
+
+ private final static int CAMERA_CLIENT_ID_0 = 0;
+ private final static int CAMERA_CLIENT_ID_1 = 1;
+ private final static int CAMERA_CLIENT_ID_DEFAULT = CAMERA_CLIENT_ID_0;
+
+ private final static int MAX_CONCURRENT_SERVICE_TYPES = 4;
+
+ private final static LinearLayout.LayoutParams LAYOUT_PARAMS_FOR_PREVIEW =
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ 1.0f
+ );
+
+ private final SparseArray<ArraySet<Integer>> mNextServiceTypes = new SparseArray<>();
+ private final SparseArray<SparseArray<CheckBox>> mCheckBoxes = new SparseArray<>();
+ private final SparseArray<CarEvsCameraClient> mCameraClients = new SparseArray<>();
+
+ private final Object mLock = new Object();
+
+ /** GL backed surface view to render the camera preview */
+ private CarEvsGLSurfaceView mEvsView;
+ private ViewGroup mRootView;
+ private LinearLayout mPreviewContainer;
+ private ViewSwitcher mPreviewSwitcher;
+
+ /** Display manager to monitor the display's state */
+ private DisplayManager mDisplayManager;
+
+ /** Current display state */
+ private int mDisplayState = Display.STATE_OFF;
+
+ /** The ID of the display we're associated with. */
+ private int mDisplayId;
+
+ @GuardedBy("mLock")
+ private Car mCar;
+
+ @GuardedBy("mLock")
+ private CarEvsManager mEvsManager;
+
+ @GuardedBy("mLock")
+ private IBinder mSessionToken;
+
+ private boolean mUseSystemWindow;
+ private int mServiceType;
+
+ /**
+ * The Activity with showWhenLocked doesn't go to sleep even if the display sleeps.
+ * So we'd like to monitor the display state and react on it manually.
+ */
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ int state = decideViewVisibility();
+ synchronized (mLock) {
+ if (state == mDisplayState) {
+ Log.i(TAG, "Already in a target state " + state);
+ return;
+ }
+
+ mDisplayState = state;
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ if (state == Display.STATE_ON) {
+ client.startVideoStream();
+ } else {
+ client.stopVideoStream();
+ }
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ String reason = extras.getString(EXTRA_DIALOG_CLOSE_REASON);
+ if (!DIALOG_CLOSE_REASON_CAR_SYSTEMBAR_BUTTON.equals(reason) &&
+ !DIALOG_CLOSE_REASON_HOME_KEY.equals(reason)) {
+ Log.i(TAG, "Ignore a request to close the system dialog with a reason = " +
+ reason);
+ return;
+ }
+ Log.d(TAG, "Requested to close the dialog, reason = " + reason);
+ }
+ finish();
+ } else {
+ Log.e(TAG, "Unexpected intent " + intent);
+ }
+ }
+ };
+
+ // To close the PreviewActiivty when Home button is clicked.
+ private void registerBroadcastReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ // Need to register the receiver for all users, because we want to receive the Intent after
+ // the user is changed.
+ registerReceiverForAllUsers(mBroadcastReceiver, filter, /* broadcastPermission= */ null,
+ /* scheduler= */ null, Context.RECEIVER_EXPORTED);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ registerBroadcastReceiver();
+ parseExtra(getIntent());
+
+
+ setShowWhenLocked(true);
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ int state = decideViewVisibility();
+
+ mDisplayId = getDisplayId();
+ mRootView = (ViewGroup) LayoutInflater.from(this).inflate(
+ R.layout.evs_preview_activity, /* root= */ null);
+
+ addCheckBoxes();
+
+ // Create a default camera client that runs CarEvsManager.SERVICE_TYPE_REARVIEW;
+ ArraySet<Integer> types = new ArraySet<>();
+ types.add(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ mCameraClients.put(CAMERA_CLIENT_ID_DEFAULT, new CarEvsCameraClient(this, types));
+
+ synchronized (mLock) {
+ mDisplayState = state;
+
+ // Packaging parameters to create CarEvsGLSurfaceView. On creation, we are running the
+ // rearview by default.
+ ArrayList<Integer> clients = new ArrayList<>();
+ clients.add(CAMERA_CLIENT_ID_DEFAULT);
+ ArraySet<Integer> serviceTypes = new ArraySet<>();
+ serviceTypes.add(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ mNextServiceTypes.put(CAMERA_CLIENT_ID_DEFAULT, serviceTypes);
+ mEvsView = createCameraViewLocked(clients);
+ }
+
+ // Add a created camera view to the view switcher.
+ mPreviewContainer = mRootView.findViewById(R.id.evs_switcher_view);
+ mPreviewContainer.addView(mEvsView, 0);
+ mPreviewSwitcher = mRootView.findViewById(R.id.evs_preview_switcher);
+
+ // Declare in and out animations and set.
+ Animation in = AnimationUtils.loadAnimation(this, android.R.anim.slide_in_left);
+ Animation out = AnimationUtils.loadAnimation(this, android.R.anim.slide_out_right);
+ mPreviewSwitcher.setInAnimation(in);
+ mPreviewSwitcher.setOutAnimation(out);
+
+ // Configure buttons.
+ View applyButton = mRootView.findViewById(R.id.apply_button);
+ if (applyButton != null) {
+ applyButton.setOnClickListener(v -> handleButtonClicked(v));
+ }
+
+ // Configure a close button.
+ View closeButton = mRootView.findViewById(R.id.close_button);
+ if (closeButton != null) {
+ closeButton.setOnClickListener(v -> handleButtonClicked(v));
+ }
+
+ int width = WindowManager.LayoutParams.MATCH_PARENT;
+ int height = WindowManager.LayoutParams.MATCH_PARENT;
+ if (mUseSystemWindow) {
+ width = getResources().getDimensionPixelOffset(R.dimen.camera_preview_width);
+ height = getResources().getDimensionPixelOffset(R.dimen.camera_preview_height);
+ }
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ width, height,
+ 2020 /* WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY */,
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ params.gravity = Gravity.CENTER;
+ params.dimAmount = getResources().getFloat(R.dimen.config_cameraBackgroundScrim);
+
+ if (mUseSystemWindow) {
+ WindowManager wm = getSystemService(WindowManager.class);
+ wm.addView(mRootView, params);
+ } else {
+ setContentView(mRootView, params);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ parseExtra(intent);
+ }
+
+ private void parseExtra(Intent intent) {
+ Bundle extras = intent.getExtras();
+
+ synchronized (mLock) {
+ if (extras == null) {
+ mSessionToken = null;
+ mServiceType = CarEvsManager.SERVICE_TYPE_REARVIEW;
+ mUseSystemWindow = false;
+ return;
+ }
+
+ mSessionToken = extras == null ?
+ null : extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN);
+ mUseSystemWindow = mSessionToken != null;
+ mServiceType = extras.getShort(Integer.toString(CarEvsManager.SERVICE_TYPE_REARVIEW));
+ }
+ }
+
+ @Override
+ protected void onRestart() {
+ Log.d(TAG, "onRestart");
+ super.onRestart();
+
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ // When we come back to the top task, we start rendering the view.
+ client.startVideoStream();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ Log.d(TAG, "onStop");
+ try {
+ if (mUseSystemWindow && mEvsView.getWindowVisibility() == View.VISIBLE) {
+ // When a new activity is launched, this activity will become the background
+ // activity and, however, likely still visible to the users if it is using the
+ // system window. Therefore, we should not transition to the STOPPED state.
+ //
+ // Similarly, this activity continues previewing the camera when the user triggers
+ // the home button. If the users want to manually close the preview window, they
+ // can trigger the close button at the bottom of the window.
+ return;
+ }
+
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ client.stopVideoStream();
+ }
+ } finally {
+ super.onStop();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy");
+ try {
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ // Request to stop current service and unregister a status listener
+ client.release();
+ }
+
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ if (mUseSystemWindow) {
+ WindowManager wm = getSystemService(WindowManager.class);
+ wm.removeViewImmediate(mRootView);
+ }
+
+ unregisterReceiver(mBroadcastReceiver);
+ } finally {
+ super.onDestroy();
+ }
+ }
+
+ // Hides the view when the display is off to save the system resource, since this has
+ // 'showWhenLocked' attribute, this will not go to PAUSED state even if the display turns off.
+ private int decideViewVisibility() {
+ Display display = mDisplayManager.getDisplay(mDisplayId);
+ int state = display.getState();
+ Log.d(TAG, "decideShowWhenLocked: displayState=" + state);
+ if (state == Display.STATE_ON) {
+ getWindow().getDecorView().setVisibility(View.VISIBLE);
+ } else {
+ getWindow().getDecorView().setVisibility(View.INVISIBLE);
+ }
+
+ return state;
+ }
+
+ private void handleButtonClicked(View v) {
+ switch (v.getId()) {
+ case R.id.close_button:
+ Toast toast = Toast.makeText(this, "Closing cameras...", Toast.LENGTH_LONG);
+ toast.addCallback(new Toast.Callback() {
+ @Override
+ public void onToastHidden() {
+ // It is possible that we've been stopped but a video stream is still
+ // active.
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ client.release();
+ }
+
+ finish();
+ }
+
+ @Override
+ public void onToastShown() { /* Nothing to do. */ }
+ });
+
+ toast.show();
+ break;
+
+ case R.id.apply_button:
+ Toast.makeText(this, "Switching the view: " + mNextServiceTypes, Toast.LENGTH_SHORT)
+ .show();
+ synchronized (mLock) {
+ ArrayList<Integer> clients = new ArrayList<>();
+ clients.add(CAMERA_CLIENT_ID_0);
+ clients.add(CAMERA_CLIENT_ID_1);
+ CarEvsGLSurfaceView view = createCameraViewLocked(clients);
+ if (view == null) {
+ // Show a blank view if createCameraViewLocked() returns null.
+ mPreviewSwitcher.addView(new View(getApplication()), -1,
+ LAYOUT_PARAMS_FOR_PREVIEW);
+ } else {
+ // Switch the view; we add a newly created view at the end, switch to it,
+ // and then remove a previous view from ViewSwitcher.
+ mPreviewSwitcher.addView(view, -1, LAYOUT_PARAMS_FOR_PREVIEW);
+ }
+ mEvsView = view;
+ View currentView = mPreviewSwitcher.getCurrentView();
+ mPreviewSwitcher.showNext();
+ mPreviewSwitcher.removeView(currentView);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private void onCheckedChangeListener(View view, boolean isChecked) {
+ int index;
+ int type;
+ switch (view.getId()) {
+ case R.id.checkbox_rearview:
+ index = CAMERA_CLIENT_ID_0;
+ type = CarEvsManager.SERVICE_TYPE_REARVIEW;
+ break;
+
+ case R.id.checkbox_frontview:
+ index = CAMERA_CLIENT_ID_0;
+ type = CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+ break;
+
+ case R.id.checkbox_leftview:
+ index = CAMERA_CLIENT_ID_0;
+ type = CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+ break;
+
+ case R.id.checkbox_rightview:
+ index = CAMERA_CLIENT_ID_0;
+ type = CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+ break;
+
+ case R.id.checkbox1_rearview:
+ index = CAMERA_CLIENT_ID_1;
+ type = CarEvsManager.SERVICE_TYPE_REARVIEW;
+ break;
+
+ case R.id.checkbox1_frontview:
+ index = CAMERA_CLIENT_ID_1;
+ type = CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+ break;
+
+ case R.id.checkbox1_leftview:
+ index = CAMERA_CLIENT_ID_1;
+ type = CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+ break;
+
+ case R.id.checkbox1_rightview:
+ index = CAMERA_CLIENT_ID_1;
+ type = CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+ break;
+
+ default:
+ return;
+ }
+
+ synchronized (mLock) {
+ if (!mNextServiceTypes.contains(index)) {
+ mNextServiceTypes.put(index, new ArraySet<>());
+ }
+
+ if (isChecked) {
+ mNextServiceTypes.get(index).add(type);
+ } else {
+ mNextServiceTypes.get(index).remove(type);
+ }
+ }
+ }
+
+ private void addCheckBoxes() {
+ // Configure checkboxes.
+ mCheckBoxes.put(CAMERA_CLIENT_ID_0, new SparseArray<CheckBox>(
+ /* capacity= */ MAX_CONCURRENT_SERVICE_TYPES));
+ CheckBox c = mRootView.findViewById(R.id.checkbox_rearview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_0).put(CarEvsManager.SERVICE_TYPE_REARVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox_frontview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_0).put(CarEvsManager.SERVICE_TYPE_FRONTVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox_leftview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_0).put(CarEvsManager.SERVICE_TYPE_LEFTVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox_rightview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_0).put(CarEvsManager.SERVICE_TYPE_RIGHTVIEW, c);
+
+ mCheckBoxes.put(CAMERA_CLIENT_ID_1, new SparseArray<CheckBox>(
+ /* capacity= */ MAX_CONCURRENT_SERVICE_TYPES));
+ c = mRootView.findViewById(R.id.checkbox1_rearview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_1).put(CarEvsManager.SERVICE_TYPE_REARVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox1_frontview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_1).put(CarEvsManager.SERVICE_TYPE_FRONTVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox1_leftview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_1).put(CarEvsManager.SERVICE_TYPE_LEFTVIEW, c);
+
+ c = mRootView.findViewById(R.id.checkbox1_rightview);
+ c.setOnCheckedChangeListener(this::onCheckedChangeListener);
+ mCheckBoxes.get(CAMERA_CLIENT_ID_1).put(CarEvsManager.SERVICE_TYPE_RIGHTVIEW, c);
+ }
+
+ @GuardedBy("mLock")
+ private CarEvsGLSurfaceView createCameraViewLocked(ArrayList<Integer> clientIds) {
+
+ // Initialize camera clients and stop video stream if it runs.
+ for (int i = 0; i < mNextServiceTypes.size(); i++) {
+ int id = mNextServiceTypes.keyAt(i);
+ CarEvsCameraClient client = mCameraClients.get(id);
+ if (client == null) {
+ client = new CarEvsCameraClient(this);
+ mCameraClients.put(id, client);
+ } else {
+ client.stopVideoStream();
+ }
+ }
+
+ // TODO(b/291770725): To avoid contentions in video stream managements on our reference
+ // hardware, we intentionally put current thread in sleep.
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ // Nothing to do.
+ }
+
+ // Create a list of CarEvsGLSurfaceView.BufferCallback for the rendering.
+ int nrows = 0;
+ ArrayList<CarEvsGLSurfaceView.BufferCallback> callbacks =
+ new ArrayList<>(/* capacity= */ clientIds.size());
+ for (int i = 0; i < mNextServiceTypes.size(); i++) {
+ int id = mNextServiceTypes.keyAt(i);
+ ArraySet types = mNextServiceTypes.valueAt(i);
+ if (types.isEmpty()) {
+ // No type is selected for current client. Uncheck all check boxes.
+ SparseArray checkBoxes = mCheckBoxes.get(id);
+ for (int j = 0; j < checkBoxes.size(); j++) {
+ ((CheckBox) checkBoxes.valueAt(j)).setChecked(false);
+ }
+ continue;
+ }
+
+ CarEvsCameraClient client = mCameraClients.get(id);
+ ArraySet<Integer> activated = client.startVideoStream(types);
+ if (activated.size() < 1) {
+ // We failed to start any service. Uncheck all check boxes.
+ SparseArray checkBoxes = mCheckBoxes.get(id);
+ for (int j = 0; j < checkBoxes.size(); j++) {
+ ((CheckBox) checkBoxes.valueAt(j)).setChecked(false);
+ }
+ continue;
+ }
+
+ // Update check boxes.
+ SparseArray checkBoxes = mCheckBoxes.get(id);
+ for (int j = 0; j < checkBoxes.size(); j++) {
+ var key = checkBoxes.keyAt(j);
+ if (activated.contains(key)) {
+ ((CheckBox) checkBoxes.get(key)).setChecked(true);
+ mNextServiceTypes.get(id).add(key);
+ } else {
+ ((CheckBox) checkBoxes.get(key)).setChecked(false);
+ mNextServiceTypes.get(id).remove(key);
+ }
+ }
+
+ callbacks.addAll(client.getBufferCallbacks());
+ ++nrows;
+ }
+
+ ArrayList<float[]> positionList = new ArrayList<>();;
+ float stride_y = 2.0f / nrows;
+
+ nrows = 0;
+ for (int i = 0; i < mCameraClients.size(); i++) {
+ CarEvsCameraClient client = mCameraClients.valueAt(i);
+ int size = client.getBufferCallbacks().size();
+ if (size < 1) {
+ continue;
+ }
+
+ float stride_x = 2.0f / size;
+ for (int j = 0; j < size; j++) {
+ float[] m = {
+ -1.0f + stride_x * j, 1.0f - stride_y * nrows, 0.0f,
+ -1.0f + stride_x * (1 + j), 1.0f - stride_y * nrows, 0.0f,
+ -1.0f + stride_x * j, 1.0f - stride_y * (1 + nrows), 0.0f,
+ -1.0f + stride_x * (1 + j), 1.0f - stride_y * (1 + nrows), 0.0f
+ };
+ positionList.add(m);
+ }
+ nrows++;
+ }
+
+ // Convert ArrayList into float[][].
+ float[][] arr = new float[positionList.size()][];
+ for (int i = 0; i < arr.length; i++) {
+ arr[i] = positionList.get(i).clone();
+ }
+ CarEvsGLSurfaceView view;
+ try {
+ view = CarEvsGLSurfaceView.create(getApplication(), callbacks,
+ getApplicationContext().getResources().getInteger(
+ R.integer.config_evsRearviewCameraInPlaneRotationAngle), arr);
+ } catch (IllegalArgumentException err) {
+ // A parameter is invalid for CarEvsGLSurfaceView instantiation.
+ Log.e(TAG, "Fail to create CarEvsGLSurfaceView.");
+ return null;
+ }
+
+ if (view != null) {
+ view.setLayoutParams(LAYOUT_PARAMS_FOR_PREVIEW);
+ }
+ return view;
+ }
+}
diff --git a/tests/CarFrameworkPackageStubsTest/Android.bp b/tests/CarFrameworkPackageStubsTest/Android.bp
new file mode 100644
index 0000000..32d090f
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 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"],
+}
+
+android_test {
+ name: "CarFrameworkPackageStubsTest",
+ srcs: ["src/**/*.java"],
+ optimize: {
+ enabled: false,
+ },
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "junit",
+ "platform-test-annotations",
+ "testng",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ test_suites: ["general-tests"],
+}
diff --git a/tests/CarFrameworkPackageStubsTest/AndroidManifest.xml b/tests/CarFrameworkPackageStubsTest/AndroidManifest.xml
new file mode 100644
index 0000000..3a5a677
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.car.frameworkpackagestubs.test"
+ android:debuggable="true">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:debuggable="true"
+ android:label="Tests for Car FrameworkPackageStubs"
+ android:targetPackage="com.android.car.frameworkpackagestubs.test" />
+
+ <!-- Allows test to query for all installed apps -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+ <application
+ android:debuggable="true"
+ android:label="CarFrameworkPackageStubsTest">
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name=".GetResultActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/CarFrameworkPackageStubsTest/AndroidTest.xml b/tests/CarFrameworkPackageStubsTest/AndroidTest.xml
new file mode 100644
index 0000000..074fa1d
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/AndroidTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Car FrameworkPackageStubs Tests">
+ <option name="config-descriptor:metadata" key="component" value="auto" />
+ <!-- Instant app do not have INTERNET permission. -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <!-- Feature is not backed by native code. -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <!-- Allow running this against a secondary user. -->
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="CarFrameworkPackageStubsTest.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop log.tag.CAR.TEST VERBOSE" />
+ <!-- Can't find the way to specify the empty string in 'value', so set the highest level -->
+ <option name="teardown-command" value="setprop log.tag.CAR.TEST ASSERT" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.car.frameworkpackagestubs.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/CarFrameworkPackageStubsTest/README.md b/tests/CarFrameworkPackageStubsTest/README.md
new file mode 100644
index 0000000..127f08c
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/README.md
@@ -0,0 +1,23 @@
+# CarFrameworkPackageStubsTest
+
+These are integration tests for [Car FrameworkPackageStubs]. Run and pass them
+in the following cases:
+
+1. A change in Car FrameworkPackageStubs
+2. A change on how a device handling [Common intents], such as: adding
+ DocumentsUI to a build target.
+
+## Running it
+
+1. Prepare the device & environment for [Atest].
+2. Run the test module by:
+
+```
+atest CarFrameworkPackageStubsTest
+```
+
+[Car FrameworkPackageStubs]: ../../FrameworkPackageStubs/README.md
+
+[Common intents]: https://developer.android.com/guide/components/intents-common
+
+[Atest]: https://source.android.com/docs/core/tests/development/atest
diff --git a/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/GetResultActivity.java b/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/GetResultActivity.java
new file mode 100644
index 0000000..ec98b29
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/GetResultActivity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.frameworkpackagestubs.test;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/** An helper activity to help test cases to startActivityForResult() and pool the result. */
+public final class GetResultActivity extends Activity {
+ private static LinkedBlockingQueue<Result> sResult;
+
+ public static class Result {
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public Result(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+ }
+
+ public static GetResultActivity startActivitySync(
+ Context context, Instrumentation instrumentation) {
+ Intent getActivityResultIntent = new Intent(context, GetResultActivity.class);
+ getActivityResultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ sResult = new LinkedBlockingQueue<>();
+ return (GetResultActivity) instrumentation.startActivitySync(getActivityResultIntent);
+ }
+
+ public int poolResultCode() {
+ Result result;
+ try {
+ result = sResult.poll(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (result == null) {
+ throw new IllegalStateException("Activity didn't receive a Result in 30 seconds");
+ }
+ return result.resultCode;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ try {
+ sResult.offer(new Result(requestCode, resultCode, data), 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/StubsTest.java b/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/StubsTest.java
new file mode 100644
index 0000000..2356acf
--- /dev/null
+++ b/tests/CarFrameworkPackageStubsTest/src/com/android/car/frameworkpackagestubs/test/StubsTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.frameworkpackagestubs.test;
+
+import static android.app.Activity.RESULT_CANCELED;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.DownloadManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Tests for Car FrameworkPackageStubs */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class StubsTest {
+ static final String TAG = "StubsTest";
+ // Undefined code other than RESULT_OK: -1, RESULT_CANCELED: 0, RESULT_FIRST_USER: 1
+ private static final int UNDEFINED_RESULT_CODE = 777;
+ // Regex to match: com.android.[deviceType].frameworkpackagestubs.Stubs[$activityName]
+ private static final Pattern REGEX_FRAMEWORK_PACKAGE_STUBS =
+ Pattern.compile("^(com\\.android\\.)(.*)(\\.frameworkpackagestubs\\.Stubs)(.*)");
+ // Regex to match: com.android.internal.app.ResolverActivity or
+ // com.android.car.activityresolver.CarResolverActivity
+ private static final Pattern REGEX_RESOLVER_ACTIVITY =
+ Pattern.compile("^(com\\.android\\.)(.*)(ResolverActivity)");
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private Instrumentation mInstrumentation;
+ private String mMimeType;
+ private String mCategory;
+ private String mData;
+ private int mExpectedResult;
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+ mPackageManager = mContext.getPackageManager();
+ mMimeType = null;
+ mCategory = null;
+ mData = null;
+ // Tests should set RESULT_CANCELED explicitly if the intent expects output.
+ mExpectedResult = UNDEFINED_RESULT_CODE;
+ }
+
+ @Test
+ public void testManageUnknownAppSources() {
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ }
+
+ @Test
+ public void testManageUnknownAppSourcesByPackage() {
+ mData = "package:com.android.car.frameworkpackagestubs.test";
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ }
+
+ @Test
+ public void testOpenDocument() {
+ mMimeType = "*/*";
+ mCategory = Intent.CATEGORY_OPENABLE;
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Intent.ACTION_OPEN_DOCUMENT);
+ }
+
+ @Test
+ public void testCreateDocument() {
+ mMimeType = "*/*";
+ mCategory = Intent.CATEGORY_OPENABLE;
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Intent.ACTION_CREATE_DOCUMENT);
+ }
+
+ @Test
+ public void testGetContent() {
+ // A media picker, etc. may support ACTION_GET_CONTENT with other MIME-types. Therefore,
+ // a non-existent MIME-type: "type/nonexistent" is used to check the general file picker.
+ mMimeType = "type/nonexistent";
+ mCategory = Intent.CATEGORY_OPENABLE;
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Intent.ACTION_GET_CONTENT);
+ }
+
+ @Test
+ public void testOpenDocumentTree() {
+ mExpectedResult = RESULT_CANCELED;
+ checkIfHandleByStub(Intent.ACTION_OPEN_DOCUMENT_TREE);
+ }
+
+ @Test
+ public void testViewDocumentRoot() {
+ mMimeType = "vnd.android.document/root";
+ checkIfHandleByStub(Intent.ACTION_VIEW);
+ }
+
+ @Test
+ public void testViewDocumentDirectory() {
+ mMimeType = "vnd.android.document/directory";
+ checkIfHandleByStub(Intent.ACTION_VIEW);
+ }
+
+ @Test
+ public void testViewDownloads() {
+ checkIfHandleByStub(DownloadManager.ACTION_VIEW_DOWNLOADS);
+ }
+
+ private void checkIfHandleByStub(String strIntent) {
+ Intent intent = new Intent(strIntent);
+ if (mMimeType != null) {
+ intent.setType(mMimeType);
+ }
+ if (mCategory != null) {
+ intent.addCategory(mCategory);
+ }
+ if (mData != null) {
+ Uri dataUri = Uri.parse(mData);
+ intent.setData(dataUri);
+ }
+
+ Log.d(TAG, "Check if frameworkpackagestubs handles " + strIntent);
+ ResolveInfo resolverInfo;
+ resolverInfo = mPackageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assertThat(resolverInfo).isNotNull();
+ ActivityInfo actInfo = resolverInfo.activityInfo;
+ assertThat(resolverInfo.activityInfo).isNotNull();
+ Log.d(TAG, "ResolveInfo.ActivityInfo.name = " + actInfo.name);
+ Matcher matchResolverActivity = REGEX_RESOLVER_ACTIVITY.matcher(actInfo.name);
+ assertWithMessage(
+ "Remove the stub or the app activity "
+ + "because only one should handle "
+ + strIntent)
+ .that(matchResolverActivity.matches())
+ .isFalse();
+ Matcher matchStubs = REGEX_FRAMEWORK_PACKAGE_STUBS.matcher(actInfo.name);
+ assertWithMessage(
+ strIntent
+ + " should be stubbed by FrameworkPackageStubs or properly validate"
+ + " "
+ + actInfo.name)
+ .that(matchStubs.matches())
+ .isTrue();
+ Log.d(TAG, "Starting " + strIntent + " should not crash.");
+ if (mExpectedResult == UNDEFINED_RESULT_CODE) {
+ // a test case needs FLAG_ACTIVITY_NEW_TASK to start an activity
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ } else {
+ Log.d(TAG, "startActivityForResult() should return " + mExpectedResult);
+ int result = startActivityForResult(intent);
+ assertWithMessage(strIntent + " should return " + mExpectedResult)
+ .that(result)
+ .isEqualTo(mExpectedResult);
+ }
+ }
+
+ private int startActivityForResult(Intent intent) {
+ GetResultActivity getResultActivity =
+ GetResultActivity.startActivitySync(mContext, mInstrumentation);
+ mInstrumentation.waitForIdleSync();
+ getResultActivity.startActivityForResult(intent, 0);
+ int resultCode = getResultActivity.poolResultCode();
+ Log.d(TAG, "startActivityForResult() returns " + resultCode);
+ return resultCode;
+ }
+}
diff --git a/tests/CarSecurityPermissionTest/Android.bp b/tests/CarSecurityPermissionTest/Android.bp
index 12e44fc..d6696f0 100644
--- a/tests/CarSecurityPermissionTest/Android.bp
+++ b/tests/CarSecurityPermissionTest/Android.bp
@@ -34,6 +34,7 @@
],
static_libs: [
+ "android.car.test.utils",
"androidx.test.core",
"androidx.test.ext.junit",
"androidx.test.rules",
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/am/CarActivityManagerPermissionTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/am/CarActivityManagerPermissionTest.java
index e9b91fb..cee6874 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/am/CarActivityManagerPermissionTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/am/CarActivityManagerPermissionTest.java
@@ -26,6 +26,8 @@
import android.car.app.CarActivityManager;
import android.car.app.CarSystemUIProxy;
import android.car.app.CarTaskViewControllerCallback;
+import android.car.test.PermissionsCheckerRule;
+import android.car.test.PermissionsCheckerRule.EnsureHasPermission;
import android.content.ComponentName;
import android.os.Binder;
import android.os.IBinder;
@@ -37,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,6 +51,8 @@
*/
@RunWith(AndroidJUnit4.class)
public class CarActivityManagerPermissionTest {
+ @Rule
+ public final PermissionsCheckerRule mPermissionsCheckerRule = new PermissionsCheckerRule();
private Car mCar;
private CarActivityManager mCarActivityManager;
@@ -145,7 +150,18 @@
}
@Test
- public void getCarTaskViewController_requiresPermission() {
+ @EnsureHasPermission(Car.PERMISSION_MANAGE_CAR_SYSTEM_UI)
+ public void getCarTaskViewController_requiresPermission_INTERACT_ACROSS_USERS() {
+ SecurityException e = assertThrows(SecurityException.class,
+ () -> mCarActivityManager.getCarTaskViewController(mock(Activity.class),
+ mock(Executor.class), mock(CarTaskViewControllerCallback.class)));
+
+ assertThat(e).hasMessageThat().contains(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ @Test
+ @EnsureHasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ public void getCarTaskViewController_requiresPermission_PERMISSION_MANAGE_CAR_SYSTEM_UI() {
SecurityException e = assertThrows(SecurityException.class,
() -> mCarActivityManager.getCarTaskViewController(mock(Activity.class),
mock(Executor.class), mock(CarTaskViewControllerCallback.class)));
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/hardware/property/CarVendorPropertyCustomPermissionTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/hardware/property/CarVendorPropertyCustomPermissionTest.java
new file mode 100644
index 0000000..9ff105d
--- /dev/null
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/hardware/property/CarVendorPropertyCustomPermissionTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.hardware.property;
+
+import static android.car.Car.PERMISSION_VENDOR_EXTENSION;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_INFO;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
+import android.car.Car;
+import android.car.VehicleAreaSeat;
+import android.car.VehicleAreaWindow;
+import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.property.CarPropertyManager;
+import android.content.Context;
+import android.os.Handler;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+
+/**
+ * This class contains tests for custom vendor permission for {@link CarPropertyManager}.
+ *
+ * This test is based on the following customize vendor permission config of the reference VHAL
+ * implementation.
+ *
+ * kMixedTypePropertyForTest:
+ * "VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO",
+ * "VehicleVendorPermission::PERMISSION_SET_VENDOR_CATEGORY_INFO",
+ * VENDOR_EXTENSION_INT_PROPERTY:
+ * "VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_SEAT",
+ * "VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE",
+ * VENDOR_EXTENSION_FLOAT_PROPERTY:
+ * "VehicleVendorPermission::PERMISSION_DEFAULT",
+ * "VehicleVendorPermission::PERMISSION_DEFAULT"
+ */
+@RunWith(AndroidJUnit4.class)
+public final class CarVendorPropertyCustomPermissionTest {
+ private static final int MIXED_TYPE_PROPERTY_FOR_TEST = 0x21e01111;
+ private static final int VENDOR_EXTENSION_INT_PROPERTY = 0x23400103;
+ private static final int VENDOR_EXTENSION_FLOAT_PROPERTY = 0x25600102;
+
+ private static final int HVAC_LEFT = VehicleAreaSeat.SEAT_ROW_1_LEFT
+ | VehicleAreaSeat.SEAT_ROW_2_LEFT | VehicleAreaSeat.SEAT_ROW_2_CENTER;
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final UiAutomation mUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ private CarPropertyManager mCarPropertyManager;
+
+ @Before
+ public void setUp() {
+ Car car = Objects.requireNonNull(Car.createCar(mContext, (Handler) null));
+ mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
+ }
+
+ private void assumePropertyIsSupported(String permission, int property) {
+ mUiAutomation.adoptShellPermissionIdentity(permission);
+
+ CarPropertyConfig<?> config = mCarPropertyManager.getCarPropertyConfig(property);
+
+ mUiAutomation.dropShellPermissionIdentity();
+
+ assumeTrue("Property: " + VehiclePropertyIds.toString(property) + " is not supported",
+ config != null);
+ }
+
+ @Test
+ public void testGetMixedTypePropertyForTest() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO,
+ MIXED_TYPE_PROPERTY_FOR_TEST);
+ mUiAutomation.adoptShellPermissionIdentity(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
+
+ mCarPropertyManager.getProperty(Object[].class, MIXED_TYPE_PROPERTY_FOR_TEST,
+ /* areaId= */ 0);
+
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testGetMixedTypePropertyForTest_noPermission() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO,
+ MIXED_TYPE_PROPERTY_FOR_TEST);
+
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.getProperty(Object[].class,
+ MIXED_TYPE_PROPERTY_FOR_TEST, /* areaId= */ 0));
+ }
+
+ @Test
+ public void testSetMixedTypePropertyForTest() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO,
+ MIXED_TYPE_PROPERTY_FOR_TEST);
+ mUiAutomation.adoptShellPermissionIdentity(PERMISSION_SET_CAR_VENDOR_CATEGORY_INFO);
+
+ mCarPropertyManager.setProperty(Object[].class, MIXED_TYPE_PROPERTY_FOR_TEST,
+ /* areaId= */ 0, /* val= */ new Object[0]);
+
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testSetMixedTypePropertyForTest_noPermission() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO,
+ MIXED_TYPE_PROPERTY_FOR_TEST);
+
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.setProperty(Object[].class,
+ MIXED_TYPE_PROPERTY_FOR_TEST, /* areaId= */ 0, /* val= */ new Object[0]));
+ }
+
+ @Test
+ public void testGetVendorExtensionIntProperty() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT,
+ VENDOR_EXTENSION_INT_PROPERTY);
+ mUiAutomation.adoptShellPermissionIdentity(PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT);
+
+ // Although we require PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT,
+ // VENDOR_EXTENSION_INT_PROPERTY is actually a window area property.
+ mCarPropertyManager.getIntProperty(VENDOR_EXTENSION_INT_PROPERTY,
+ VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD);
+
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testGetVendorExtensionIntProperty_noPermissions() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT,
+ VENDOR_EXTENSION_INT_PROPERTY);
+
+ // Although we require PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT,
+ // VENDOR_EXTENSION_INT_PROPERTY is actually a window area property.
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.getIntProperty(
+ VENDOR_EXTENSION_INT_PROPERTY, VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD));
+ }
+
+ @Test
+ public void testSetVendorExtensionIntProperty() {
+ assumePropertyIsSupported(PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT,
+ VENDOR_EXTENSION_INT_PROPERTY);
+
+ // The set permission is PERMISSION_NOT_ACCESSIBLE.
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.setIntProperty(
+ VENDOR_EXTENSION_INT_PROPERTY, VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD,
+ /* val= */ 0));
+ }
+
+ @Test
+ public void testGetVendorExtensionFloatProperty() {
+ assumePropertyIsSupported(PERMISSION_VENDOR_EXTENSION,
+ VENDOR_EXTENSION_FLOAT_PROPERTY);
+ mUiAutomation.adoptShellPermissionIdentity(PERMISSION_VENDOR_EXTENSION);
+
+ mCarPropertyManager.getFloatProperty(VENDOR_EXTENSION_FLOAT_PROPERTY, HVAC_LEFT);
+
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testGetVendorExtensionFloatProperty_noPermission() {
+ assumePropertyIsSupported(PERMISSION_VENDOR_EXTENSION,
+ VENDOR_EXTENSION_FLOAT_PROPERTY);
+
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.getFloatProperty(
+ VENDOR_EXTENSION_FLOAT_PROPERTY, HVAC_LEFT));
+ }
+
+ @Test
+ public void testSetVendorExtensionFloatProperty() {
+ assumePropertyIsSupported(PERMISSION_VENDOR_EXTENSION,
+ VENDOR_EXTENSION_FLOAT_PROPERTY);
+ mUiAutomation.adoptShellPermissionIdentity(PERMISSION_VENDOR_EXTENSION);
+
+ mCarPropertyManager.setFloatProperty(VENDOR_EXTENSION_FLOAT_PROPERTY, HVAC_LEFT,
+ /* val= */ 0f);
+
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testSetVendorExtensionFloatProperty_noPermission() {
+ assumePropertyIsSupported(PERMISSION_VENDOR_EXTENSION,
+ VENDOR_EXTENSION_FLOAT_PROPERTY);
+
+ assertThrows(SecurityException.class, () -> mCarPropertyManager.setFloatProperty(
+ VENDOR_EXTENSION_FLOAT_PROPERTY, HVAC_LEFT,
+ /* val= */ 0f));
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index b8e8b54..695678f 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -230,6 +230,9 @@
<!-- use for BiometricPromptTestFragment to test the API -->
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
+ <!-- To view dumpsys output -->
+ <uses-permission android:name="android.permission.DUMP"/>
+
<!-- Allow backup is set to false for KitchenSinkApp to avoid infinite loop during backup -->
<application android:label="@string/app_title"
android:allowBackup="false"
@@ -432,17 +435,5 @@
<action android:name="android.car.remoteaccess.RemoteTaskClientService" />
</intent-filter>
</service>
-
- <service
- android:name=".autofill.KitchenSinkAutofillService"
- android:label="KS Autofill"
- android:permission="android.permission.BIND_AUTOFILL_SERVICE"
- android:exported="true">
-
- <intent-filter>
- <action android:name="android.service.autofill.AutofillService" />
- </intent-filter>
- </service>
-
</application>
</manifest>
diff --git a/tests/EmbeddedKitchenSinkApp/res/drawable/architecture.xml b/tests/EmbeddedKitchenSinkApp/res/drawable/architecture.xml
new file mode 100644
index 0000000..5e3c7e6
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/drawable/architecture.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<vector android:height="24dp" android:tint="#BD2525"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M6.36,18.78L6.61,21l1.62,-1.54l2.77,-7.6c-0.68,-0.17 -1.28,-0.51 -1.77,-0.98L6.36,18.78z"/>
+ <path android:fillColor="@android:color/white" android:pathData="M14.77,10.88c-0.49,0.47 -1.1,0.81 -1.77,0.98l2.77,7.6L17.39,21l0.26,-2.22L14.77,10.88z"/>
+ <path android:fillColor="@android:color/white" android:pathData="M15,8c0,-1.3 -0.84,-2.4 -2,-2.82V3h-2v2.18C9.84,5.6 9,6.7 9,8c0,1.66 1.34,3 3,3S15,9.66 15,8zM12,9c-0.55,0 -1,-0.45 -1,-1c0,-0.55 0.45,-1 1,-1s1,0.45 1,1C13,8.55 12.55,9 12,9z"/>
+</vector>
diff --git a/tests/EmbeddedKitchenSinkApp/res/drawable/archive.xml b/tests/EmbeddedKitchenSinkApp/res/drawable/archive.xml
new file mode 100644
index 0000000..93a513e
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/drawable/archive.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<vector android:height="24dp" android:tint="#25BD5A"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
+</vector>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml b/tests/EmbeddedKitchenSinkApp/res/drawable/audiotrack.xml
similarity index 60%
copy from car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
copy to tests/EmbeddedKitchenSinkApp/res/drawable/audiotrack.xml
index 8ad0860..445fe5d 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitSettingsRRO/res/values-port/colors.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/drawable/audiotrack.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,6 +13,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="top_level_preference_text_color">@color/car_on_primary_container</color>
-</resources>
+
+<vector android:height="24dp" android:tint="#B772D2"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
index 71f1dcf..eb41a2e 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
@@ -228,6 +228,14 @@
android:layout_margin="10dp"
android:text="Custom Group Without Summary of 6 (Should not group)"
android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/actions_with_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Notification with custom action icons"
+ android:textSize="30sp"/>
</LinearLayout>
<LinearLayout
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/oemcarservice.xml b/tests/EmbeddedKitchenSinkApp/res/layout/oemcarservice.xml
new file mode 100644
index 0000000..1c212c4
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/oemcarservice.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_focus"/>
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <Button
+ android:id="@+id/oem_car_service_audio_focus_test_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_focus_test"
+ android:padding="20dp"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_volume"/>
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <Button
+ android:id="@+id/oem_car_service_audio_volume_test_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_volume_test"
+ android:padding="20dp"/>
+ </LinearLayout>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="20dp"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_focus_results"/>
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:id="@+id/oem_car_service_audio_focus_text"
+ android:layout_width="400dp"
+ android:layout_height="wrap_content"
+ android:text="No Results"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_car_service_audio_volume_results"/>
+ <Space
+ android:layout_width="3dp"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:id="@+id/oem_car_service_audio_volume_text"
+ android:layout_width="400dp"
+ android:layout_height="wrap_content"
+ android:text="No Results"/>
+ </LinearLayout>
+ </LinearLayout>
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="3dp"/>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Instructions for installing OEMCarServiceTestApp:\n
+ adb root\n
+ adb remount #(reboot if required)\n
+ adb shell setprop persist.com.android.car.internal.debug.oem_car_service\n
+ com.android.car.oemcarservice.testapp/.OemCarServiceImpl\n
+ m -j OemCarServiceTestApp\n
+ adb root && adb remount && adb shell stop && adb sync &&\n
+ adb shell start\n
+ Visit go/oem-customization-service for more information."/>
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="20dp"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Ensure that the following have been set:\n
+ adb shell setprop persist.log.tag.CarOemAudioFocusProxyService DEBUG\n
+ adb shell setprop persist.log.tag.CarOemAudioVolumeProxyService DEBUG"/>
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 366d1c0..81d0070 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -466,6 +466,16 @@
<!-- Drive Mode Switch -->
<string name="drive_mode_state" translatable="false">Drive Mode State</string>
+ <!-- Oem Car Service Test -->
+ <string name="oem_car_service_audio_focus" translatable="false">Car Oem Audio Focus Proxy Service </string>
+ <string name="oem_car_service_audio_volume" translatable="false">Car Oem Audio Volume Proxy Service</string>
+ <string name="oem_car_service_audio_focus_test" translatable="false">Test Audio Focus Request</string>
+ <string name="oem_car_service_audio_volume_test" translatable="false">Test Volume Key Event</string>
+ <string name="oem_car_service_audio_focus_results" translatable="false"><b>Audio Focus Request Result</b></string>
+ <string name="oem_car_service_audio_volume_results" translatable="false"><b>Volume Key Events Result</b></string>
+ <string name="oem_car_service_no_results" translatable="false">There were no log results to display.</string>
+ <string name="oem_car_service_average_execution_time" translatable="false">Average function execution time: "%.3f (ms)</string>
+
<!-- Privacy chip test -->
<string name="audio_permission_granted">Audio permission granted</string>
<string name="audio_permission_not_granted">No audio permission</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 090b436..6baea84 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -56,6 +56,7 @@
import com.google.android.car.kitchensink.audio.AudioTestFragment;
import com.google.android.car.kitchensink.audio.AudioUserAssignmentFragment;
import com.google.android.car.kitchensink.audio.CarAudioInputTestFragment;
+import com.google.android.car.kitchensink.audio.OemCarServiceTestFragment;
import com.google.android.car.kitchensink.audiorecorder.AudioRecorderTestFragment;
import com.google.android.car.kitchensink.backup.BackupAndRestoreFragment;
import com.google.android.car.kitchensink.biometrics.BiometricPromptTestFragment;
@@ -295,7 +296,8 @@
new FragmentMenuEntry("inject motion", InjectMotionTestFragment.class),
new FragmentMenuEntry("inject key", InjectKeyTestFragment.class),
new FragmentMenuEntry("window insets full screen",
- WindowInsetsFullScreenFragment.class));
+ WindowInsetsFullScreenFragment.class),
+ new FragmentMenuEntry("oem car service", OemCarServiceTestFragment.class));
private Car mCarApi;
private CarHvacManager mHvacManager;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioMirrorTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioMirrorTestFragment.java
index b30a836..0c344f0 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioMirrorTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioMirrorTestFragment.java
@@ -109,11 +109,19 @@
showToast("Must select two distinct zones to mirror");
return;
}
- if (isZoneCurrentlyMirroring(zoneOne) || isZoneCurrentlyMirroring(zoneTwo)) {
+ if (isZoneCurrentlyMirroringOrCastingToPrimaryZone(zoneOne)
+ || isZoneCurrentlyMirroringOrCastingToPrimaryZone(zoneTwo)) {
return;
}
- int status = mCarAudioManager.canEnableAudioMirror();
+ int status;
+ try {
+ status = mCarAudioManager.canEnableAudioMirror();
+ } catch (Exception e) {
+ showToast("Error while enabling mirror: " + e.getMessage());
+ return;
+ }
+
if (status != AUDIO_MIRROR_CAN_ENABLE) {
showToast("Can not enable any more zones for mirroring, status " + status);
return;
@@ -133,12 +141,21 @@
showToast("Requested mirroring for audio zones [" + zoneOne + ", " + zoneTwo + "]");
}
- private boolean isZoneCurrentlyMirroring(int zoneOne) {
- List<Integer> mirroringZones = mCarAudioManager.getMirrorAudioZonesForAudioZone(zoneOne);
+ private boolean isZoneCurrentlyMirroringOrCastingToPrimaryZone(int audioZoneId) {
+ List<Integer> mirroringZones = mCarAudioManager.getMirrorAudioZonesForAudioZone(
+ audioZoneId);
if (mirroringZones.size() != 0) {
- showToast("Zone " + zoneOne + " is already mirroring");
+ showToast("Zone " + audioZoneId + " is already mirroring");
return true;
}
+
+ OccupantZoneInfo info = mCarOccupantZoneManager.getOccupantForAudioZoneId(audioZoneId);
+ if (mCarAudioManager.isMediaAudioAllowedInPrimaryZone(info)) {
+ showToast("Can not enable mirror, occupant in audio zone " + audioZoneId
+ + " is currently playing audio in primary zone");
+ return true;
+ }
+
return false;
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioUserAssignmentFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioUserAssignmentFragment.java
index 55ceb45..73ddc2f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioUserAssignmentFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioUserAssignmentFragment.java
@@ -19,6 +19,7 @@
import static android.R.layout.simple_spinner_dropdown_item;
import static android.R.layout.simple_spinner_item;
import static android.car.CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER;
+import static android.car.media.CarAudioManager.AUDIO_FEATURE_AUDIO_MIRRORING;
import static android.car.media.CarAudioManager.INVALID_REQUEST_ID;
import android.car.Car;
@@ -41,6 +42,7 @@
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.Spinner;
+import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
@@ -188,16 +190,13 @@
Log.d(TAG, "handleToggleAssignUserAudio");
int position = mUserSpinner.getSelectedItemPosition();
int userId = mUserAdapter.getItem(position);
+ OccupantZoneInfo info = getOccupantZoneForUser(userId);
- boolean isUserAssigned =
- mCarAudioManager.isMediaAudioAllowedInPrimaryZone(getOccupantZoneForUser(userId));
-
- Log.d(TAG, "handleToggleAssignUserAudio is user assigned " + isUserAssigned);
-
- if (isUserAssigned) {
- Log.d(TAG, "handleToggleAssignUserAudio user is already assigned");
+ if (mRequestIdToOccupantZone.containsValue(info)) {
+ handleCancelMediaAudioOnPrimaryZone(info);
return;
}
+
handleRequestUserToPlayInMainCabin(userId);
}
@@ -206,11 +205,21 @@
OccupantZoneInfo info = getOccupantZoneForUser(userId);
if (info == null) {
Log.e(TAG, "Can not find occupant zone info for user" + userId);
+ showToast("User " + userId + " is not currently assigned to any occupant zone");
return;
}
- if (mRequestIdToOccupantZone.containsValue(info)) {
- handleCancelMediaAudioOnPrimaryZone(info);
+ if (mCarAudioManager.isMediaAudioAllowedInPrimaryZone(info)) {
+ showToast("User " + userId + " is already allowed to play in primary zone");
+ return;
+ }
+
+ int carAudioZoneId = mCarOccupantZoneManager.getAudioZoneIdForOccupant(info);
+
+ if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING)
+ && !mCarAudioManager.getMirrorAudioZonesForAudioZone(carAudioZoneId).isEmpty()) {
+ showToast("Can not enable primary zone playback as user " + userId
+ + " is currently mirroring with another zone");
return;
}
@@ -219,10 +228,15 @@
private void requestToPlayAudioInPrimaryZone(OccupantZoneInfo info) {
Log.d(TAG, "requestUserToPlayInMainCabin occupant " + info);
- long requestId =
- mCarAudioManager.requestMediaAudioOnPrimaryZone(info,
- ContextCompat.getMainExecutor(getActivity().getApplicationContext()),
- mCallback);
+ long requestId;
+ try {
+ requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(info,
+ ContextCompat.getMainExecutor(getActivity().getApplicationContext()),
+ mCallback);
+ } catch (Exception e) {
+ showToast("Error while requesting media playback: " + e.getMessage());
+ return;
+ }
if (requestId == INVALID_REQUEST_ID) {
handleRequestRejected(info);
return;
@@ -267,21 +281,29 @@
return null;
}
- private void handleCancelMediaAudioOnPrimaryZone(
- OccupantZoneInfo info) {
+ private void handleCancelMediaAudioOnPrimaryZone(OccupantZoneInfo info) {
Log.d(TAG, "handleCancelMediaAudioOnPrimaryZone");
int index = mRequestIdToOccupantZone.indexOfValue(info);
if (index < 0) {
- Log.d(TAG, "handleCancelMediaAudioOnPrimaryZone user not assigned");
+ showToast("Occupant " + info + " not currently assign to play media in primary zone");
return;
}
+
long requestId = mRequestIdToOccupantZone.keyAt(index);
+
mRequestIdToOccupantZone.remove(requestId);
- if (!mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId)) {
- Log.d(TAG, "handleCancelMediaAudioOnPrimaryZone could not unassigned");
+ boolean cancelled;
+ try {
+ cancelled = mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId);
+ } catch (Exception e) {
+ showToast("Could not cancel media on primary zone: " + e.getMessage());
return;
}
- Log.d(TAG, "handleCancelMediaAudioOnPrimaryZone could unassigned");
+ if (!cancelled) {
+ showToast("Could not unassigned request " + requestId + " for occupant " + info);
+ return;
+ }
+ showToast("Unassigned request " + requestId + " for occupant " + info);
mToggleUserAssignButton.setText(R.string.assign_user);
}
@@ -317,6 +339,7 @@
private void onCarReady(Car car, boolean ready) {
Log.i(TAG, String.format("connectCar ready %b", ready));
if (!ready) {
+ showToast("Car service not ready!");
return;
}
@@ -356,6 +379,11 @@
occupantZoneInfo));
continue;
}
+
+ // Do not include driver in the list as driver already owns the primary zone
+ if (occupantZoneInfo.occupantType == OCCUPANT_TYPE_DRIVER) {
+ continue;
+ }
Log.i(TAG, String.format("setUserInfo occupant zone %s has user %d",
occupantZoneInfo, userId));
userList.add(userId);
@@ -365,6 +393,11 @@
counter++;
}
+ if (userList.isEmpty()) {
+ showToast("Audio playback to primary zone is not supported on this device");
+ return;
+ }
+
Integer[] userArray = userList.toArray(Integer[]::new);
mUserAdapter = new ArrayAdapter<>(mContext, simple_spinner_item, userArray);
mUserAdapter.setDropDownViewResource(simple_spinner_dropdown_item);
@@ -372,4 +405,9 @@
mUserSpinner.setEnabled(true);
mUserSpinner.setSelection(myIndex);
}
+
+ private void showToast(String message) {
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ Log.d(TAG, "Showed toast message: " + message);
+ }
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/OemCarServiceTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/OemCarServiceTestFragment.java
new file mode 100644
index 0000000..353073d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/OemCarServiceTestFragment.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.audio;
+
+import static android.media.AudioManager.AUDIOFOCUS_GAIN;
+import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+
+import android.car.Car;
+import android.car.CarOccupantZoneManager;
+import android.content.Context;
+import android.media.AudioFocusRequest;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.kitchensink.R;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public final class OemCarServiceTestFragment extends Fragment {
+ private static final String TAG = OemCarServiceTestFragment.class.getSimpleName();
+ private static final int TEST_ITERATIONS = 10;
+ private Context mContext;
+ private Car mCar;
+ private AudioManager mAudioManager;
+ private VolumeKeyEventsButtonManager mVolumeKeyEventHandler;
+ private final ExecutorService mPool = Executors.newFixedThreadPool(TEST_ITERATIONS);
+ private TextView mAudioFocusResultText;
+ private TextView mAudioVolumeResultText;
+
+ private void connectCar() {
+ mContext = getContext();
+ mCar = Car.createCar(mContext, /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ });
+
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ Log.i(TAG, "onCreateView");
+ View view = inflater.inflate(R.layout.oemcarservice, container, /* attachRoot= */ false);
+
+ connectCar();
+
+ mVolumeKeyEventHandler = new VolumeKeyEventsButtonManager(
+ mCar.getCarManager(CarOccupantZoneManager.class));
+
+ view.findViewById(R.id.oem_car_service_audio_volume_test_button).setOnClickListener(v -> {
+ sendVolumeKeyEvent();
+ });
+
+ view.findViewById(R.id.oem_car_service_audio_focus_test_button).setOnClickListener(v -> {
+ sendAudioFocusRequest();
+ });
+
+ mAudioFocusResultText = view.findViewById(R.id.oem_car_service_audio_focus_text);
+
+ mAudioVolumeResultText = view.findViewById(R.id.oem_car_service_audio_volume_text);
+
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ Log.i(TAG, "onViewCreated");
+ super.onViewCreated(view, savedInstanceState);
+ }
+
+ @Override
+ public void onDestroyView() {
+ Log.i(TAG, "onDestroyView");
+
+ if (mCar != null && mCar.isConnected()) {
+ mCar.disconnect();
+ mCar = null;
+ }
+ super.onDestroyView();
+ }
+
+ private void sendAudioFocusRequest() {
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ MediaWithDelayedFocusListener mediaWithDelayedFocusListener =
+ new MediaWithDelayedFocusListener();
+ AudioFocusRequest mediaAudioFocusRequest = new AudioFocusRequest.Builder(
+ AUDIOFOCUS_GAIN)
+ .setAcceptsDelayedFocusGain(true).setOnAudioFocusChangeListener(
+ mediaWithDelayedFocusListener).build();
+ int delayedFocusRequestResults = mAudioManager.requestAudioFocus(
+ mediaAudioFocusRequest);
+
+ if (delayedFocusRequestResults != AUDIOFOCUS_REQUEST_GRANTED) {
+ Log.i(TAG, "sendAudioFocusRequest not granted " + delayedFocusRequestResults);
+ }
+ }
+
+ dump("CarOemAudioFocusProxyService", mAudioFocusResultText);
+ Toast.makeText(mContext,
+ "Test Finished for Audio Focus Requests with " + TEST_ITERATIONS
+ + " iterations.", Toast.LENGTH_SHORT).show();
+ }
+
+ private void sendVolumeKeyEvent() {
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ mPool.execute(() -> mVolumeKeyEventHandler
+ .sendClickEvent(KeyEvent.KEYCODE_VOLUME_UP));
+ }
+
+ dump("CarOemAudioVolumeProxyService", mAudioVolumeResultText);
+ Toast.makeText(mContext, "Test Finished for Volume Key Events with " + TEST_ITERATIONS
+ + " iterations.", Toast.LENGTH_SHORT).show();
+ }
+
+ private void dump(String header, TextView textView) {
+ Process dump;
+ try {
+ dump = Runtime.getRuntime().exec("dumpsys car_service --oem-service");
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot flush", e);
+ return;
+ }
+
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(dump.getInputStream()))) {
+ String line = "";
+ boolean captureDump = false;
+ int sum = 0;
+ float iterations = 0;
+ while ((line = reader.readLine()) != null) {
+ // End of execution time dump.
+ if (captureDump) {
+ if (line.contains("time log complete")) {
+ break;
+ }
+ if (line.contains("startTime, duration")) {
+ continue;
+ }
+ if (line.contains(",")) {
+ sum += Integer.parseInt(line.split(",")[1].trim());
+ iterations++;
+ }
+ }
+ if (line.contains(header)) {
+ captureDump = true;
+ }
+ }
+ if (iterations == 0) {
+ textView.setText(getResources().getString(R.string.oem_car_service_no_results));
+ return;
+ }
+ textView.setText(String.format(
+ getResources().getString(R.string.oem_car_service_average_execution_time),
+ sum / iterations));
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot flush", e);
+ }
+ }
+
+ private static final class MediaWithDelayedFocusListener implements
+ AudioManager.OnAudioFocusChangeListener {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.i(TAG, "Focus changed" + focusChange);
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
index fb95419..a6d5ba6 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
@@ -107,6 +107,7 @@
initCustomGroupSummaryButton(view);
initGroupWithoutSummaryButton(view);
initCustomizableMessageButton(view);
+ initButtonWithCustomActionIcon(view);
return view;
}
@@ -893,4 +894,36 @@
}
});
}
+
+ private void initButtonWithCustomActionIcon(View view) {
+ Intent intent = new Intent(mContext, KitchenSinkActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ Notification notification = new Notification
+ .Builder(mContext, IMPORTANCE_HIGH_ID)
+ .setContentTitle("Notification with custom button icon")
+ .setContentText(
+ "Icons should be shown in the action buttons")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .addAction(
+ new Notification.Action.Builder(
+ R.drawable.architecture, "architecture", pendingIntent)
+ .build())
+ .addAction(
+ new Notification.Action.Builder(
+ Icon.createWithResource(this.getContext(), R.drawable.archive),
+ "archive", pendingIntent).build())
+ .addAction(
+ new Notification.Action.Builder(
+ Icon.createWithResource(this.getContext().getPackageName(),
+ R.drawable.audiotrack),
+ "audio-track", pendingIntent).build())
+ .setColor(mContext.getColor(android.R.color.holo_red_light))
+ .build();
+
+ view.findViewById(R.id.actions_with_icons).setOnClickListener(
+ v -> mManager.notify(mCurrentNotificationId++, notification)
+ );
+ }
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/watchdog/CarWatchdogTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/watchdog/CarWatchdogTestFragment.java
index 62e281d..8d21d04 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/watchdog/CarWatchdogTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/watchdog/CarWatchdogTestFragment.java
@@ -451,6 +451,8 @@
return "I/O overuse warning notification";
case NOTIFICATION_TYPE_OVERUSE:
return "I/O overuse exceeding notification";
+ default:
+ Log.e(TAG, "Invalid notification type: " + type);
}
return "Unknown notification type";
}
diff --git a/tests/MultiDisplaySecondaryHomeTestLauncher/src/com/android/car/multidisplay/launcher/LauncherActivity.java b/tests/MultiDisplaySecondaryHomeTestLauncher/src/com/android/car/multidisplay/launcher/LauncherActivity.java
index ca648e3..41125e0 100644
--- a/tests/MultiDisplaySecondaryHomeTestLauncher/src/com/android/car/multidisplay/launcher/LauncherActivity.java
+++ b/tests/MultiDisplaySecondaryHomeTestLauncher/src/com/android/car/multidisplay/launcher/LauncherActivity.java
@@ -102,7 +102,9 @@
private final CarOccupantZoneManager.OccupantZoneConfigChangeListener mConfigChangeListener =
flags -> {
if ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) != 0) {
- launchOtherUiForInvalidUser(mCar);
+ if (isVisibleForAutofill()) {
+ launchOtherUiForInvalidUser(mCar);
+ }
}
};
@@ -110,6 +112,13 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ WallpaperManager wallpaperMgr = getSystemService(WallpaperManager.class);
+ mIsWallpaperSupported = wallpaperMgr != null && wallpaperMgr.isWallpaperSupported();
+ int userId = getUserId();
+ int displayId = getDisplayId();
+ Log.d(TAG, "Creating for user " + userId + " on display " + displayId
+ + ". Wallpaper supported: " + mIsWallpaperSupported);
+
Car.createCar(/* context= */ this, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
(car, ready) -> {
mCar = car;
@@ -125,12 +134,6 @@
launchOtherUiForInvalidUser(car);
});
- WallpaperManager wallpaperMgr = getSystemService(WallpaperManager.class);
- mIsWallpaperSupported = wallpaperMgr != null && wallpaperMgr.isWallpaperSupported();
- int userId = getUserId();
- int displayId = getDisplayId();
- Log.d(TAG, "Creating for user " + userId + " on display " + displayId
- + ". Wallpaper supported: " + mIsWallpaperSupported);
setContentView(R.layout.activity_main);
mRootView = findViewById(R.id.RootView);
@@ -308,28 +311,28 @@
return;
}
UserManager userManager = getSystemService(UserManager.class);
+ if (!userManager.isVisibleBackgroundUsersSupported()) {
+ Log.i(TAG, "No visible background users are supported");
+ return;
+ }
// After this point, we either should launch user picker or home
boolean shouldLaunchUserPicker = false;
if (assignedUserId == UserHandle.USER_NULL) {
Log.w(TAG, "No assigned user for Display#" + myDisplayId);
shouldLaunchUserPicker = true;
} else if (userManager.isUserUnlocked(UserHandle.of(assignedUserId))) {
- Log.i(TAG, "Will start HOME for User:" + assignedUserId + ",Display#:"
- + myDisplayId);
+ Log.i(TAG, "User " + assignedUserId + " is unlocked for Display#" + myDisplayId
+ + ": finish itself to allow ATM to bring up the correct HOME");
shouldLaunchUserPicker = false;
} else {
Log.i(TAG, "Will start user picker as user is not unlocked, User:" + assignedUserId
- + ",Display#:" + myDisplayId);
+ + ", Display#:" + myDisplayId);
shouldLaunchUserPicker = true;
}
if (shouldLaunchUserPicker) {
CarActivityManager am = (CarActivityManager) car.getCarManager(
Car.CAR_ACTIVITY_SERVICE);
am.startUserPickerOnDisplay(myDisplayId);
- } else {
- Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(
- Intent.CATEGORY_SECONDARY_HOME).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityAsUser(intent, UserHandle.of(assignedUserId));
}
finish();
}
diff --git a/tests/MultiDisplayTest/Android.bp b/tests/MultiDisplayTest/Android.bp
index ffe7ec9..6535e22 100644
--- a/tests/MultiDisplayTest/Android.bp
+++ b/tests/MultiDisplayTest/Android.bp
@@ -22,7 +22,10 @@
name: "MultiDisplayTest",
// Only compile source java files in this apk.
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.proto",
+ ],
enforce_uses_libs: false,
dex_preopt: {
@@ -41,4 +44,6 @@
"androidx.recyclerview_recyclerview",
],
+ required: ["allowed_privapp_com.google.android.car.multidisplaytest"],
+
}
diff --git a/tests/MultiDisplayTest/AndroidManifest.xml b/tests/MultiDisplayTest/AndroidManifest.xml
index cd1b3f8..0179cb6 100644
--- a/tests/MultiDisplayTest/AndroidManifest.xml
+++ b/tests/MultiDisplayTest/AndroidManifest.xml
@@ -21,6 +21,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.car.multidisplaytest">
+
+ <!-- Used for OccupantConnectionFragment -->
+ <uses-permission android:name="android.car.permission.MANAGE_OCCUPANT_CONNECTION"/>
+ <uses-permission android:name="android.car.permission.MANAGE_REMOTE_DEVICE"/>
+
<application android:label="MD Test">
<activity android:name="MDTest"
android:label="@string/app_title_always"
@@ -32,5 +37,15 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name=".occupantconnection.PermissionActivity"/>
+
+ <service android:name=".occupantconnection.ReceiverService"
+ android:permission="android.car.occupantconnection.permission.BIND_RECEIVER_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.car.intent.action.RECEIVER_SERVICE" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/tests/MultiDisplayTest/res/layout/occupant_connection_fragment.xml b/tests/MultiDisplayTest/res/layout/occupant_connection_fragment.xml
new file mode 100644
index 0000000..2d5b09d
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/occupant_connection_fragment.xml
@@ -0,0 +1,78 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="3"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Discoverer"
+ android:textSize="36sp"/>
+ <Button
+ android:id="@+id/register_state_callback"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="registerStateCallback()"/>
+ <Button
+ android:id="@+id/unregister_state_callback"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="unregisterStateCallback()"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="List of peer zones:"/>
+ <LinearLayout
+ android:id="@+id/peer_zones"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Receivers"
+ android:textSize="36sp"/>
+ <Button
+ android:id="@+id/text_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="TextReceiver"/>
+ <Button
+ android:id="@+id/progress_bar_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="ProgressBarReceiver"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/occupant_connection_permission_activity.xml b/tests/MultiDisplayTest/res/layout/occupant_connection_permission_activity.xml
new file mode 100644
index 0000000..66d101c
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/occupant_connection_permission_activity.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/positive"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Accept"/>
+ <Button
+ style="?android:attr/buttonBarButtonStyle"
+ android:id="@+id/negative"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Reject"/>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/seek_bar_receiver_fragment.xml b/tests/MultiDisplayTest/res/layout/seek_bar_receiver_fragment.xml
new file mode 100644
index 0000000..46df209
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/seek_bar_receiver_fragment.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="Back"/>
+ <SeekBar
+ android:id="@+id/seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/sender_fragment.xml b/tests/MultiDisplayTest/res/layout/sender_fragment.xml
new file mode 100644
index 0000000..d4ffcef
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/sender_fragment.xml
@@ -0,0 +1,78 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Sender"
+ android:textSize="36sp"/>
+ <Button
+ android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="Back"/>
+ <Button
+ android:id="@+id/request_connection"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="requestConnection()"/>
+ <Button
+ android:id="@+id/cancel_connection"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="cancelConnection()"/>
+ <Button
+ android:id="@+id/disconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="disconnect()"/>
+ <Button
+ android:id="@+id/is_connected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="isConnected()"/>
+ <LinearLayout
+ android:id="@+id/received_text_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <EditText
+ android:id="@+id/edit_text"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:hint="Hello world"
+ android:importantForAutofill="no"/>
+ <Button
+ android:id="@+id/send_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="Send text"/>
+ </LinearLayout>
+ <SeekBar
+ android:id="@+id/seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/text_receiver_fragment.xml b/tests/MultiDisplayTest/res/layout/text_receiver_fragment.xml
new file mode 100644
index 0000000..a798e64
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/text_receiver_fragment.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="false"
+ android:text="Back"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Received text"
+ android:textSize="36sp"/>
+ <LinearLayout
+ android:id="@+id/received_text_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
index b93283e..236a527 100644
--- a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
@@ -33,6 +33,7 @@
import com.google.android.car.multidisplaytest.draw.DrawTestFragment;
import com.google.android.car.multidisplaytest.ime.InputTestFragment;
+import com.google.android.car.multidisplaytest.occupantconnection.OccupantConnectionFragment;
import com.google.android.car.multidisplaytest.present.PresentTestFragment;
import com.google.android.car.multidisplaytest.touch.TouchTestFragment;
@@ -112,8 +113,8 @@
new FragmentMenuEntry("Touch test", TouchTestFragment.class),
new FragmentMenuEntry("IME test", InputTestFragment.class),
new FragmentMenuEntry("Draw test", DrawTestFragment.class),
- new FragmentMenuEntry("Present test", PresentTestFragment.class)
- );
+ new FragmentMenuEntry("Present test", PresentTestFragment.class),
+ new FragmentMenuEntry("Occupant Connection", OccupantConnectionFragment.class));
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/Constants.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/Constants.java
new file mode 100644
index 0000000..f1dd688
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/Constants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+final class Constants {
+
+ private Constants() {
+ }
+
+ static final String KEY_OCCUPANT_ZONE = "occupant_zone";
+ static final String KEY_MESSAGE_RECEIVER = "message_receiver";
+ static final String SYSTEM_PROPERTY_KEY_CONNECTION_NEEDS_USER_APPROVAL =
+ "multidisplaytest.occupantconnection.need_approval";
+ static final String ACTION_CONNECTION_CANCELLED =
+ "multidisplaytest.occupantconnection.connection_cancelled";
+ static final String TEXT_RECEIVER_ID = "TextReceiverFragment";
+ static final String SEEK_BAR_RECEIVER_ID = "SeekBarReceiverFragment";
+
+ static final int ACCEPTED_RESULT_CODE = 1;
+ static final int REJECTED_RESULT_CODE = 2;
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/OccupantConnectionFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/OccupantConnectionFragment.java
new file mode 100644
index 0000000..c95f0f9
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/OccupantConnectionFragment.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_INSTALLED;
+import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_IN_FOREGROUND;
+import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_RUNNING;
+import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_SAME_LONG_VERSION;
+import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_SAME_SIGNATURE;
+import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_CONNECTION_READY;
+import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_POWER_ON;
+import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.CarOccupantZoneManager.OccupantZoneInfo;
+import android.car.CarRemoteDeviceManager;
+import android.car.CarRemoteDeviceManager.StateCallback;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.google.android.car.multidisplaytest.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OccupantConnectionFragment extends Fragment {
+
+ private static final int INITIAL_ZONE_STATE = 0;
+ private static final int INITIAL_APP_STATE = 0;
+
+ // The member variables are accessed by the main thread only, so there is no multi-thread issue.
+ private final ArrayMap<OccupantZoneInfo, Integer> mOccupantZoneStateMap = new ArrayMap<>();
+ private final ArrayMap<OccupantZoneInfo, Integer> mAppStateMap = new ArrayMap<>();
+ private final List<OccupantZoneInfo> mAddedPeerZones = new ArrayList<>();
+
+ private String mTag;
+ private CarRemoteDeviceManager mRemoteDeviceManager;
+ private FragmentActivity mActivity;
+ private ViewGroup mPeerZonesLayout;
+ private boolean mIsDiscovering;
+
+ private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ Slog.e(mTag, "Disconnect from Car Service");
+ mRemoteDeviceManager = null;
+ return;
+ }
+ Slog.v(mTag, "Connected to Car Service");
+ mRemoteDeviceManager = car.getCarManager(CarRemoteDeviceManager.class);
+ };
+
+ private final StateCallback mStateCallback = new StateCallback() {
+ @Override
+ public void onOccupantZoneStateChanged(
+ @androidx.annotation.NonNull OccupantZoneInfo occupantZone,
+ int occupantZoneStates) {
+ Slog.d(mTag, "onOccupantZoneStateChanged: occupantZone=" + occupantZone
+ + ", occupantZoneStates=" + occupantZoneStates);
+ mOccupantZoneStateMap.put(occupantZone, occupantZoneStates);
+ if (mAddedPeerZones.contains(occupantZone)) {
+ updateOccupantZoneState(occupantZone, occupantZoneStates);
+ return;
+ }
+ addPeerOccupantZoneItem(occupantZone, occupantZoneStates, INITIAL_ZONE_STATE);
+ mAddedPeerZones.add(occupantZone);
+ }
+
+ @Override
+ public void onAppStateChanged(
+ @androidx.annotation.NonNull OccupantZoneInfo occupantZone,
+ int appStates) {
+ Slog.d(mTag, "onAppStateChanged: occupantZone=" + occupantZone
+ + ", appStates=" + appStates);
+ mAppStateMap.put(occupantZone, appStates);
+
+ if (!mAddedPeerZones.contains(occupantZone)) {
+ // onOccupantZoneStateChanged() is guaranteed to be invoked before
+ // onAppStateChanged() for a given occupant zone, so this must be a bug when it
+ // happens.
+ throw new IllegalStateException("onOccupantZoneStateChanged() should have been "
+ + "invoked for " + occupantZone + " already");
+ }
+ updateAppState(occupantZone, appStates);
+ }
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Car.createCar(getContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
+ mActivity = getActivity();
+ mTag = "OccupantConnection##" + mActivity.getUserId();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(
+ R.layout.occupant_connection_fragment, container, /* attachToRoot= */false);
+ mPeerZonesLayout = root.findViewById(R.id.peer_zones);
+
+ Button registerStateCallbackButton = root.findViewById(R.id.register_state_callback);
+ registerStateCallbackButton.setOnClickListener(v -> {
+ if (mIsDiscovering) {
+ Toast.makeText(mActivity, "It is discovering already", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (!isRemoteDeviceManagerReady()) return;
+ mRemoteDeviceManager.registerStateCallback(mActivity.getMainExecutor(), mStateCallback);
+ Slog.d(mTag, "Start discovery");
+ mIsDiscovering = true;
+ });
+
+ Button unregisterStateCallbackButton = root.findViewById(R.id.unregister_state_callback);
+ unregisterStateCallbackButton.setOnClickListener(v -> {
+ if (!mIsDiscovering) {
+ Toast.makeText(mActivity, "It is not discovering", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (!isRemoteDeviceManagerReady()) return;
+ stopDiscovery();
+ });
+
+ Button textReceiverButton = root.findViewById(R.id.text_receiver);
+ textReceiverButton.setOnClickListener(v -> {
+ Fragment textReceiverFragment = new TextReceiverFragment();
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, textReceiverFragment)
+ .commit();
+ });
+
+ Button seekBarReceiverButton = root.findViewById(R.id.progress_bar_receiver);
+ seekBarReceiverButton.setOnClickListener(v -> {
+ Fragment seekBarReceiverFragment = new SeekBarReceiverFragment();
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, seekBarReceiverFragment)
+ .commit();
+ });
+
+ return root;
+ }
+
+ @Override
+ public void onDestroyView() {
+ stopDiscovery();
+ super.onDestroyView();
+ }
+
+ private void addPeerOccupantZoneItem(OccupantZoneInfo zone, int occupantZoneStates,
+ int appStates) {
+ Button powerOnButton = new Button(mActivity);
+ powerOnButton.setText("Power on");
+ powerOnButton.setOnClickListener(v -> {
+ if (!isRemoteDeviceManagerReady()) return;
+ if (mRemoteDeviceManager.isOccupantZonePowerOn(zone)) {
+ Toast.makeText(mActivity, "The occupant zone is already powered on",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mRemoteDeviceManager.setOccupantZonePower(zone, true);
+ Slog.d(mTag, "Power on " + zone);
+ });
+
+ Button powerOffButton = new Button(mActivity);
+ powerOffButton.setText("Power off");
+ powerOffButton.setOnClickListener(v -> {
+ if (!isRemoteDeviceManagerReady()) return;
+ if (!mRemoteDeviceManager.isOccupantZonePowerOn(zone)) {
+ Toast.makeText(mActivity, "The occupant zone is already powered off",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mRemoteDeviceManager.setOccupantZonePower(zone, false);
+ Slog.d(mTag, "Power off " + zone);
+ });
+
+ Button connectButton = new Button(mActivity);
+ connectButton.setText("Connect");
+ connectButton.setOnClickListener(v -> {
+ int currentZoneState = mOccupantZoneStateMap.getOrDefault(zone, INITIAL_ZONE_STATE);
+ int currentAppState = mAppStateMap.getOrDefault(zone, INITIAL_APP_STATE);
+ if (checkConnectionError(currentZoneState, currentAppState)) {
+ return;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Constants.KEY_OCCUPANT_ZONE, zone);
+ Fragment senderFragment = new SenderFragment();
+ senderFragment.setArguments(bundle);
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, senderFragment)
+ .commit();
+ });
+
+ LinearLayout buttonParent = new LinearLayout(mActivity);
+ buttonParent.addView(powerOnButton);
+ buttonParent.addView(powerOffButton);
+ buttonParent.addView(connectButton);
+
+ TextView stateView = new TextView(mActivity);
+ setStateView(stateView, zone, occupantZoneStates, appStates);
+
+ LinearLayout zoneLayout = new LinearLayout(mActivity);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ );
+ params.setMargins(20, 20, 0, 20);
+ zoneLayout.setLayoutParams(params);
+ zoneLayout.setBackgroundColor(Color.BLACK);
+ zoneLayout.setOrientation(LinearLayout.VERTICAL);
+ zoneLayout.addView(buttonParent);
+ zoneLayout.addView(stateView);
+ zoneLayout.setTag(zone.zoneId);
+
+ mPeerZonesLayout.addView(zoneLayout);
+ }
+
+ private void updateOccupantZoneState(OccupantZoneInfo zone, int occupantZoneStates) {
+ TextView stateView = getStateViewForOccupantZone(zone);
+ int appStates = mAppStateMap.getOrDefault(zone, INITIAL_APP_STATE);
+ setStateView(stateView, zone, occupantZoneStates, appStates);
+ }
+
+ private void updateAppState(OccupantZoneInfo zone, int appStates) {
+ TextView stateView = getStateViewForOccupantZone(zone);
+ int zoneStates = mOccupantZoneStateMap.getOrDefault(zone, INITIAL_ZONE_STATE);
+ setStateView(stateView, zone, zoneStates, appStates);
+ }
+
+ private TextView getStateViewForOccupantZone(OccupantZoneInfo zone) {
+ for (int i = 0; i < mPeerZonesLayout.getChildCount(); i++) {
+ ViewGroup zoneLayout = (ViewGroup) mPeerZonesLayout.getChildAt(i);
+ Integer zoneId = (Integer) zoneLayout.getTag();
+ if (zoneId.intValue() == zone.zoneId) {
+ return (TextView) zoneLayout.getChildAt(1);
+ }
+ }
+ throw new IllegalArgumentException("The TextView for " + zone + " was not added before");
+ }
+
+ private static void setStateView(TextView textView, OccupantZoneInfo zone,
+ int occupantZoneStates, int appStates) {
+ StringBuilder builder = new StringBuilder()
+ .append(zone)
+ .append((occupantZoneStates & FLAG_OCCUPANT_ZONE_POWER_ON) != 0
+ ? ":\nscreens on, " : ":\nscreens off, ")
+ .append((occupantZoneStates & FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED) != 0
+ ? "main screen unlocked, " : "main screen locked, ")
+ .append((occupantZoneStates & FLAG_OCCUPANT_ZONE_CONNECTION_READY) != 0
+ ? "connection ready\n" : "connection not ready\n");
+
+ if ((occupantZoneStates & FLAG_OCCUPANT_ZONE_CONNECTION_READY) == 0) {
+ builder.append("app state unknown");
+ } else if ((appStates & FLAG_CLIENT_INSTALLED) == 0) {
+ builder.append("app not installed");
+ } else {
+ builder.append((appStates & FLAG_CLIENT_SAME_LONG_VERSION) != 0
+ ? "same version " : "different long version ");
+ builder.append((appStates & FLAG_CLIENT_SAME_SIGNATURE) != 0
+ ? "same signature app " : "different signature app ");
+ if ((appStates & FLAG_CLIENT_RUNNING) == 0) {
+ builder.append("installed but not running");
+ } else {
+ builder.append((appStates & FLAG_CLIENT_IN_FOREGROUND) != 0
+ ? "running in foreground" : "running in background");
+ }
+ }
+ String text = builder.toString();
+ textView.setText(text);
+ }
+
+ /** Checks connection error and returns true if there is connection error. */
+ private boolean checkConnectionError(int occupantZoneStates, int appStates) {
+ String connectionError = getConnectionError(occupantZoneStates, appStates);
+ if (connectionError != null) {
+ Toast.makeText(mActivity,
+ "Can't connect to the occupant zone because " + connectionError,
+ Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ return false;
+ }
+
+ private static String getConnectionError(int occupantZoneStates, int appStates) {
+ if ((occupantZoneStates & FLAG_OCCUPANT_ZONE_POWER_ON) == 0) {
+ // Note: if you don't need user confirmation to establish the connection, just skip this
+ // block and next block, and change ReceiverService#NEEDS_USER_APPROVAL to be false.
+ return "it is not powered on";
+ }
+ if ((occupantZoneStates & FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED) == 0) {
+ // TODO(b/277234550): ignore screen locked state for now since it is not implemented
+ // yet.
+ // Note: if you don't need user confirmation to establish the connection, just skip this
+ // block and the previous block, and change ReceiverService#NEEDS_USER_APPROVAL to be
+ // false.
+ // return "its main screen is locked";
+ }
+ if ((occupantZoneStates & FLAG_OCCUPANT_ZONE_CONNECTION_READY) == 0) {
+ return "it is not ready for connection";
+ }
+ if ((appStates & FLAG_CLIENT_INSTALLED) == 0) {
+ return "the peer app is not installed";
+ }
+ if ((appStates & FLAG_CLIENT_SAME_LONG_VERSION) == 0) {
+ return "the peer app has a different long version";
+ }
+ if ((appStates & FLAG_CLIENT_SAME_SIGNATURE) == 0) {
+ return "the peer app has a different signature";
+ }
+ if ((appStates & FLAG_CLIENT_RUNNING) == 0) {
+ // Note: if you want the sender to connect to the receiver even if the receiver is not
+ // running, just skip this block.
+ return "the peer app is not running";
+ }
+ if ((appStates & FLAG_CLIENT_IN_FOREGROUND) == 0) {
+ // Note: if you want the sender to connect to the receiver even if the receiver is not
+ // running in the foreground, just skip this block.
+ return "the peer app is running in background";
+ }
+ return null;
+ }
+
+ private boolean isRemoteDeviceManagerReady() {
+ if (mRemoteDeviceManager != null) return true;
+ Toast.makeText(mActivity, "No CarRemoteDeviceManager", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ private void stopDiscovery() {
+ if (mIsDiscovering) {
+ mRemoteDeviceManager.unregisterStateCallback();
+ Slog.d(mTag, "Discovery has stopped");
+ mIsDiscovering = false;
+ }
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PayloadUtils.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PayloadUtils.java
new file mode 100644
index 0000000..54ed1fb
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PayloadUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import android.car.occupantconnection.Payload;
+
+import com.google.android.car.multidisplaytest.occupantconnection.PayloadProto.Data;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+/** Utility methods for serializing and deserializing the Payload. */
+class PayloadUtils {
+
+ private PayloadUtils() {
+ }
+
+ static Payload createPayload(String receiverEndpointId, String text) {
+ Data data = Data.newBuilder()
+ .setReceiverEndpointId(receiverEndpointId)
+ .setText(text)
+ .build();
+ return new Payload(data.toByteArray());
+ }
+
+ static Payload createPayload(String receiverEndpointId, int progress) {
+ Data data = Data.newBuilder()
+ .setReceiverEndpointId(receiverEndpointId)
+ .setProgress(progress)
+ .build();
+ return new Payload(data.toByteArray());
+ }
+
+ static Data parseData(Payload payload) {
+ try {
+ return Data.parseFrom(payload.getBytes());
+ } catch (InvalidProtocolBufferException e) {
+ throw new IllegalArgumentException("Invalid payload: " + e);
+ }
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PermissionActivity.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PermissionActivity.java
new file mode 100644
index 0000000..97a3160
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/PermissionActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.ACCEPTED_RESULT_CODE;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.ACTION_CONNECTION_CANCELLED;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.KEY_MESSAGE_RECEIVER;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.REJECTED_RESULT_CODE;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Slog;
+import android.widget.Button;
+
+import androidx.annotation.Nullable;
+
+import com.google.android.car.multidisplaytest.R;
+
+public class PermissionActivity extends Activity {
+
+ private String mTag;
+ private ResultReceiver mResultReceiver;
+
+ private final BroadcastReceiver mConnectionCancellationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_CONNECTION_CANCELLED.equals(intent.getAction())) {
+ Slog.d(mTag, "Connection request was cancelled by the sender");
+ finish();
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.occupant_connection_permission_activity);
+ mTag = "OccupantConnection##" + getUserId();
+ mResultReceiver = getIntent().getParcelableExtra(KEY_MESSAGE_RECEIVER);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Button positiveButton = findViewById(R.id.positive);
+ positiveButton.setOnClickListener(v -> {
+ Slog.d(mTag, "User approved the connection request");
+ mResultReceiver.send(ACCEPTED_RESULT_CODE, null);
+ finish();
+ });
+ Button negativeButton = findViewById(R.id.negative);
+ negativeButton.setOnClickListener(v -> {
+ Slog.d(mTag, "User rejected the connection request");
+ mResultReceiver.send(REJECTED_RESULT_CODE, null);
+ finish();
+ });
+
+ IntentFilter intentFilter = new IntentFilter(ACTION_CONNECTION_CANCELLED);
+ registerReceiver(mConnectionCancellationReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+ }
+
+ @Override
+ protected void onPause() {
+ unregisterReceiver(mConnectionCancellationReceiver);
+ super.onPause();
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/ReceiverService.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/ReceiverService.java
new file mode 100644
index 0000000..d47f723
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/ReceiverService.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static android.car.occupantconnection.CarOccupantConnectionManager.CONNECTION_ERROR_USER_REJECTED;
+
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.ACCEPTED_RESULT_CODE;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.ACTION_CONNECTION_CANCELLED;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.KEY_MESSAGE_RECEIVER;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.REJECTED_RESULT_CODE;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.SEEK_BAR_RECEIVER_ID;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.SYSTEM_PROPERTY_KEY_CONNECTION_NEEDS_USER_APPROVAL;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.TEXT_RECEIVER_ID;
+
+import android.car.CarOccupantZoneManager.OccupantZoneInfo;
+import android.car.occupantconnection.AbstractReceiverService;
+import android.car.occupantconnection.Payload;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.car.multidisplaytest.occupantconnection.PayloadProto.Data;
+
+/**
+ * {@inheritDoc}
+ *
+ * Run `adb shell setprop multidisplaytest.occupantconnection.need_approval false` to disable user
+ * approval, and `adb shell setprop multidisplaytest.occupantconnection.need_approval true` to
+ * enable it. By default, it is enabled. When it is enabled, the connection request must be approved
+ * by the user explicitly. Otherwise, it will be accepted automatically without user interaction.
+ */
+public class ReceiverService extends AbstractReceiverService {
+
+ // The member variables are accessed by the main thread only, so there is no multi-thread issue.
+ private String mTag;
+ private OccupantZoneInfo mCachedSenderZone;
+ private Payload mCachedPayload;
+
+ /** Used by PermissionActivity to send the user confirmation result back to this service. */
+ class MessageReceiver extends ResultReceiver {
+
+ private final OccupantZoneInfo mSenderZone;
+
+ MessageReceiver(OccupantZoneInfo senderZone) {
+ super(/* handler= */ null);
+ mSenderZone = senderZone;
+ }
+
+ /** Called when there's a result available. */
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case ACCEPTED_RESULT_CODE:
+ Slog.d(mTag, "Accept connection from sender " + mSenderZone
+ + " because the user has approved it");
+ acceptConnection(mSenderZone);
+ break;
+ case REJECTED_RESULT_CODE:
+ Slog.d(mTag, "Reject connection from sender " + mSenderZone
+ + " because the user has rejected it");
+ rejectConnection(mSenderZone, CONNECTION_ERROR_USER_REJECTED);
+ break;
+ default:
+ throw new IllegalStateException(mTag + " Unknown resultCode: " + resultCode);
+ }
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ mTag = "OccupantConnection##" + getUserId();
+ super.onCreate();
+ }
+
+ /**
+ * If the receiver endpoint has been registered, forward the Payload immediately after
+ * receiving it. Otherwise, abandon the Payload for TextReceiverFragment, but cache the Payload
+ * for SeekBarReceiverFragment.
+ */
+ @Override
+ public void onPayloadReceived(@NonNull OccupantZoneInfo senderZone,
+ @NonNull Payload payload) {
+ Data data = PayloadUtils.parseData(payload);
+ String receiverEndpointId = data.getReceiverEndpointId();
+ Slog.d(mTag, "onPayloadReceived:senderZone=" + senderZone
+ + ", receiverEndpointId=" + receiverEndpointId);
+ if (TEXT_RECEIVER_ID.equals(receiverEndpointId)) {
+ boolean success = forwardPayload(senderZone, receiverEndpointId, payload);
+ if (success) {
+ Slog.d(mTag, "Payload has been forwarded to " + receiverEndpointId);
+ } else {
+ Slog.d(mTag, "Abandon the payload because " + receiverEndpointId
+ + " is not registered yet");
+ }
+ return;
+ }
+ if (SEEK_BAR_RECEIVER_ID.equals(receiverEndpointId)) {
+ boolean success = forwardPayload(senderZone, receiverEndpointId, payload);
+ if (success) {
+ Slog.d(mTag, "Payload has been forwarded to " + receiverEndpointId);
+ } else {
+ Slog.d(mTag, "Cache the payload because " + receiverEndpointId
+ + " is not registered yet");
+ mCachedSenderZone = senderZone;
+ mCachedPayload = payload;
+ }
+ return;
+ }
+ throw new IllegalStateException("Unknown receiverEndpointId: " + receiverEndpointId);
+ }
+
+ @Override
+ public void onReceiverRegistered(@NonNull String receiverEndpointId) {
+ Slog.d(mTag, "onReceiverRegistered(" + receiverEndpointId + ")");
+ if (SEEK_BAR_RECEIVER_ID.equals(receiverEndpointId) && mCachedSenderZone != null
+ && mCachedPayload != null) {
+ forwardPayload(mCachedSenderZone, SEEK_BAR_RECEIVER_ID, mCachedPayload);
+ }
+ }
+
+ @Override
+ public void onConnectionInitiated(@NonNull OccupantZoneInfo senderZone) {
+ boolean needsUserApproval = SystemProperties.getBoolean(
+ SYSTEM_PROPERTY_KEY_CONNECTION_NEEDS_USER_APPROVAL, /* def= */ true);
+ Slog.d(mTag, "onConnectionInitiated:senderZone=" + senderZone
+ + ", needsUserApproval=" + needsUserApproval);
+ if (needsUserApproval) {
+ Intent intent = new Intent(this, PermissionActivity.class);
+ // Pack the parcelable receiver into the intent extras so PermissionActivity can
+ // access it.
+ intent.putExtra(KEY_MESSAGE_RECEIVER, new MessageReceiver(senderZone));
+ // Calling startActivity() from outside an Activity context requires the
+ // FLAG_ACTIVITY_NEW_TASK flag.
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Note: startActivityForResult() doesn't work from a service. That's why we have to
+ // use the MessageReceiver to get the activity result.
+ startActivity(intent);
+ } else {
+ Slog.d(mTag, "Accept connection from sender " + senderZone + " automatically");
+ acceptConnection(senderZone);
+ }
+ }
+
+ @Override
+ public void onConnected(@NonNull OccupantZoneInfo senderZone) {
+ Slog.d(mTag, "onConnected(" + senderZone + ")");
+ }
+
+ @Override
+ public void onConnectionCanceled(@NonNull OccupantZoneInfo senderZone) {
+ Slog.d(mTag, "onConnectionCanceled(" + senderZone + ")");
+ sendBroadcast(new Intent(ACTION_CONNECTION_CANCELLED));
+ }
+
+ @Override
+ public void onDisconnected(@NonNull OccupantZoneInfo senderZone) {
+ Slog.d(mTag, "onDisconnected(" + senderZone + ")");
+ }
+
+ @Override
+ public void onDestroy() {
+ Slog.d(mTag, "onDestroy()");
+ super.onDestroy();
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SeekBarReceiverFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SeekBarReceiverFragment.java
new file mode 100644
index 0000000..379d73d
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SeekBarReceiverFragment.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.SEEK_BAR_RECEIVER_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.occupantconnection.CarOccupantConnectionManager;
+import android.car.occupantconnection.CarOccupantConnectionManager.PayloadCallback;
+import android.os.Bundle;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.google.android.car.multidisplaytest.R;
+import com.google.android.car.multidisplaytest.occupantconnection.PayloadProto.Data;
+
+public class SeekBarReceiverFragment extends Fragment {
+
+ private String mTag;
+ private CarOccupantConnectionManager mOccupantConnectionManager;
+ private FragmentActivity mActivity;
+ private View mRoot;
+ private Button mBackButton;
+ private SeekBar mSeekBar;
+
+ private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ Slog.e(mTag, "Disconnect from Car Service");
+ mOccupantConnectionManager = null;
+ return;
+ }
+ Slog.v(mTag, "Connected to Car Service");
+ mOccupantConnectionManager = car.getCarManager(CarOccupantConnectionManager.class);
+ };
+
+ private final PayloadCallback mPayloadCallback = (senderZone, payload) -> {
+ Data data = PayloadUtils.parseData(payload);
+ payload.close();
+ int progress = data.getProgress();
+ Slog.d(mTag, "Received progress " + progress + " from " + senderZone);
+ mSeekBar.setProgress(progress);
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Car.createCar(getContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
+ mActivity = getActivity();
+ mTag = "OccupantConnection##" + mActivity.getUserId();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ mRoot = inflater.inflate(R.layout.seek_bar_receiver_fragment, container,
+ /* attachToRoot= */false);
+ mBackButton = mRoot.findViewById(R.id.back);
+ mSeekBar = mRoot.findViewById(R.id.seek_bar);
+ init();
+ return mRoot;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mOccupantConnectionManager != null) {
+ Slog.d(mTag, "unregisterReceiver(" + SEEK_BAR_RECEIVER_ID + ")");
+ mOccupantConnectionManager.unregisterReceiver(SEEK_BAR_RECEIVER_ID);
+ }
+ super.onDestroy();
+ }
+
+ private void init() {
+ mBackButton.setOnClickListener(v -> {
+ Fragment occupantConnectionFragment = new OccupantConnectionFragment();
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, occupantConnectionFragment)
+ .commit();
+ });
+
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Slog.d(mTag, "registerReceiver(" + SEEK_BAR_RECEIVER_ID + ")");
+ mOccupantConnectionManager.registerReceiver(SEEK_BAR_RECEIVER_ID,
+ mActivity.getMainExecutor(), mPayloadCallback);
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SenderFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SenderFragment.java
new file mode 100644
index 0000000..d445393
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/SenderFragment.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.SEEK_BAR_RECEIVER_ID;
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.TEXT_RECEIVER_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.CarOccupantZoneManager.OccupantZoneInfo;
+import android.car.occupantconnection.CarOccupantConnectionManager;
+import android.car.occupantconnection.CarOccupantConnectionManager.ConnectionRequestCallback;
+import android.car.occupantconnection.Payload;
+import android.os.Bundle;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.google.android.car.multidisplaytest.R;
+
+
+public class SenderFragment extends Fragment {
+
+ private static final int STATE_NOT_CONNECTED = 0;
+ private static final int STATE_CONNECTING = 1;
+ private static final int STATE_CONNECTED = 2;
+
+ // The member variables are accessed by the main thread only, so there is no multi-thread issue.
+ private String mTag;
+ private CarOccupantConnectionManager mOccupantConnectionManager;
+ private OccupantZoneInfo mReceiverZone;
+ private FragmentActivity mActivity;
+ private int mConnectionState;
+
+ private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ Slog.e(mTag, "Disconnect from Car Service");
+ mOccupantConnectionManager = null;
+ return;
+ }
+ Slog.v(mTag, "Connected to Car Service");
+ mOccupantConnectionManager = car.getCarManager(CarOccupantConnectionManager.class);
+ };
+
+ private final ConnectionRequestCallback mRequestCallback = new ConnectionRequestCallback() {
+ @Override
+ public void onConnected(OccupantZoneInfo receiverZone) {
+ Toast.makeText(mActivity, "Connected", Toast.LENGTH_SHORT).show();
+ mConnectionState = STATE_CONNECTED;
+ }
+
+ @Override
+ public void onFailed(OccupantZoneInfo receiverZone, int connectionError) {
+ Toast.makeText(mActivity, "Connection request failed", Toast.LENGTH_SHORT).show();
+ mConnectionState = STATE_NOT_CONNECTED;
+ }
+
+ @Override
+ public void onDisconnected(OccupantZoneInfo receiverZone) {
+ Toast.makeText(mActivity, "Disconnected by the receiver", Toast.LENGTH_SHORT).show();
+ mConnectionState = STATE_NOT_CONNECTED;
+ }
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Car.createCar(getContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
+ mActivity = getActivity();
+ mTag = "OccupantConnection##" + mActivity.getUserId();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ mReceiverZone = getArguments().getParcelable(Constants.KEY_OCCUPANT_ZONE);
+ View root = inflater.inflate(R.layout.sender_fragment, container, /* attachToRoot= */false);
+
+ Button backButtton = root.findViewById(R.id.back);
+ backButtton.setOnClickListener(v -> {
+ Fragment occupantConnectionFragment = new OccupantConnectionFragment();
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, occupantConnectionFragment)
+ .commit();
+ });
+
+ Button requestConnectionButton = root.findViewById(R.id.request_connection);
+ requestConnectionButton.setOnClickListener(v -> {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState == STATE_CONNECTING) {
+ Toast.makeText(mActivity, "Already requested a connection",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState == STATE_CONNECTED) {
+ Toast.makeText(mActivity, "Already connected",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mOccupantConnectionManager.requestConnection(mReceiverZone, mActivity.getMainExecutor(),
+ mRequestCallback);
+ mConnectionState = STATE_CONNECTING;
+ Slog.d(mTag, "Requesting connection to " + mReceiverZone);
+ });
+
+ Button cancelConnectionButton = root.findViewById(R.id.cancel_connection);
+ cancelConnectionButton.setOnClickListener(v -> {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState != STATE_CONNECTING) {
+ Toast.makeText(mActivity, "No pending connection request to cancel",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mOccupantConnectionManager.cancelConnection(mReceiverZone);
+ mConnectionState = STATE_NOT_CONNECTED;
+ Slog.d(mTag, "Connection request was canceled by us");
+ });
+
+ Button disconnectButton = root.findViewById(R.id.disconnect);
+ disconnectButton.setOnClickListener(v -> {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState != STATE_CONNECTED) {
+ Toast.makeText(mActivity, "Can't disconnect because it was not connected",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mOccupantConnectionManager.disconnect(mReceiverZone);
+ mConnectionState = STATE_NOT_CONNECTED;
+ Slog.d(mTag, "Disconnected by us");
+ });
+
+ Button isConnectedButton = root.findViewById(R.id.is_connected);
+ isConnectedButton.setOnClickListener(v -> {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ boolean isConnected = mOccupantConnectionManager.isConnected(mReceiverZone);
+ isConnectedButton.setText("is connected? " + isConnected);
+ });
+
+ EditText editText = root.findViewById(R.id.edit_text);
+
+ Button sendTextButton = root.findViewById(R.id.send_text);
+ sendTextButton.setOnClickListener(v -> {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState != STATE_CONNECTED) {
+ Toast.makeText(mActivity, "Can't send text because it was not connected",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String textToSend = editText.getText().toString();
+ Payload payload = PayloadUtils.createPayload(TEXT_RECEIVER_ID, textToSend);
+ try {
+ mOccupantConnectionManager.sendPayload(mReceiverZone, payload);
+ Slog.d(mTag, "Message [" + textToSend + "] has been sent to " + mReceiverZone);
+ } catch (CarOccupantConnectionManager.PayloadTransferException e) {
+ Toast.makeText(mActivity, "Failed to send the message",
+ Toast.LENGTH_SHORT).show();
+ Slog.e(mTag, "Failed to send message [" + textToSend + "] to " + mReceiverZone);
+ }
+ payload.close();
+ });
+
+ SeekBar seekBar = root.findViewById(R.id.seek_bar);
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mConnectionState != STATE_CONNECTED) {
+ Toast.makeText(mActivity,
+ "Can't send the progress because it was not connected",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Payload payload = PayloadUtils.createPayload(SEEK_BAR_RECEIVER_ID, progress);
+ try {
+ mOccupantConnectionManager.sendPayload(mReceiverZone, payload);
+ Slog.d(mTag, "Progress " + progress + " has been sent to " + mReceiverZone);
+ } catch (CarOccupantConnectionManager.PayloadTransferException e) {
+ Toast.makeText(mActivity, "Failed to send the progress",
+ Toast.LENGTH_SHORT).show();
+ Slog.e(mTag, "Failed to send progress " + progress + " to " + mReceiverZone);
+ }
+ payload.close();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mOccupantConnectionManager != null) {
+ if (mConnectionState == STATE_CONNECTING) {
+ mOccupantConnectionManager.cancelConnection(mReceiverZone);
+ } else if (mConnectionState == STATE_CONNECTED) {
+ mOccupantConnectionManager.disconnect(mReceiverZone);
+ }
+ mConnectionState = STATE_NOT_CONNECTED;
+ }
+ super.onDestroy();
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/TextReceiverFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/TextReceiverFragment.java
new file mode 100644
index 0000000..04d468a
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/TextReceiverFragment.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.multidisplaytest.occupantconnection;
+
+import static com.google.android.car.multidisplaytest.occupantconnection.Constants.TEXT_RECEIVER_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.occupantconnection.CarOccupantConnectionManager;
+import android.car.occupantconnection.CarOccupantConnectionManager.PayloadCallback;
+import android.os.Bundle;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.google.android.car.multidisplaytest.R;
+import com.google.android.car.multidisplaytest.occupantconnection.PayloadProto.Data;
+
+public class TextReceiverFragment extends Fragment {
+
+ private String mTag;
+ private CarOccupantConnectionManager mOccupantConnectionManager;
+ private FragmentActivity mActivity;
+ private ViewGroup mReceivedTextContainer;
+
+ private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ Slog.e(mTag, "Disconnect from Car Service");
+ mOccupantConnectionManager = null;
+ return;
+ }
+ Slog.v(mTag, "Connected to Car Service");
+ mOccupantConnectionManager = car.getCarManager(CarOccupantConnectionManager.class);
+ };
+
+ private final PayloadCallback mPayloadCallback = (senderZone, payload) -> {
+ Data data = PayloadUtils.parseData(payload);
+ payload.close();
+ String message = data.getText();
+ Slog.d(mTag, "Received message " + message + " from " + senderZone);
+ String text = "Message from " + senderZone + ":\n" + message;
+ int zoneId = senderZone.zoneId;
+ TextView textView;
+ for (int i = 0; i < mReceivedTextContainer.getChildCount(); i++) {
+ textView = (TextView) mReceivedTextContainer.getChildAt(i);
+ Object tag = textView.getTag();
+ if (tag != null) {
+ Integer value = (Integer) tag;
+ if (value.intValue() == zoneId) {
+ textView.setText(text);
+ return;
+ }
+ }
+ }
+ textView = new TextView(mActivity);
+ textView.setTag(zoneId);
+ textView.setText(text);
+ mReceivedTextContainer.addView(textView);
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Car.createCar(getContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
+ mActivity = getActivity();
+ mTag = "OccupantConnection##" + mActivity.getUserId();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.text_receiver_fragment, container,
+ /* attachToRoot= */false);
+ Button backButton = root.findViewById(R.id.back);
+ mReceivedTextContainer = root.findViewById(R.id.received_text_container);
+ backButton.setOnClickListener(v -> {
+ Fragment occupantConnectionFragment = new OccupantConnectionFragment();
+ mActivity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.menu_content, occupantConnectionFragment)
+ .commit();
+ });
+
+ if (mOccupantConnectionManager == null) {
+ Toast.makeText(mActivity, "No CarOccupantConnectionManager",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Slog.d(mTag, "registerReceiver(" + TEXT_RECEIVER_ID + ")");
+ mOccupantConnectionManager.registerReceiver(TEXT_RECEIVER_ID,
+ mActivity.getMainExecutor(),
+ mPayloadCallback);
+ }
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mOccupantConnectionManager != null) {
+ Slog.d(mTag, "unregisterReceiver(" + TEXT_RECEIVER_ID + ")");
+ mOccupantConnectionManager.unregisterReceiver(TEXT_RECEIVER_ID);
+ }
+ super.onDestroy();
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/payload.proto b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/payload.proto
new file mode 100644
index 0000000..e25cea2
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/occupantconnection/payload.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+syntax = "proto3";
+package com.google.android.car.multidisplaytest.occupantconnection;
+option java_package = "com.google.android.car.multidisplaytest.occupantconnection";
+option java_outer_classname = "PayloadProto";
+message Data {
+ string receiver_endpoint_id = 1;
+ // The text sent to TextReceiverFragment.
+ optional string text = 2;
+ // The progress of the SeekBar sent to SeekBarReceiverFragment.
+ optional int32 progress = 3;
+}
diff --git a/tests/ThemePlayground/src/com/android/car/themeplayground/DialogSamples.java b/tests/ThemePlayground/src/com/android/car/themeplayground/DialogSamples.java
index d6bba34..41b2817 100644
--- a/tests/ThemePlayground/src/com/android/car/themeplayground/DialogSamples.java
+++ b/tests/ThemePlayground/src/com/android/car/themeplayground/DialogSamples.java
@@ -55,7 +55,7 @@
if (showCheckbox) {
// Set Custom Title
- TextView title = new TextView(this);
+ TextView title = new TextView(builder.getContext());
// Title Properties
title.setText("Custom Dialog Box");
builder.setCustomTitle(title);
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDevicePolicyManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDevicePolicyManagerTest.java
index 4fc6b4f..8b415a9 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDevicePolicyManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDevicePolicyManagerTest.java
@@ -45,6 +45,8 @@
private static final String TAG = CarDevicePolicyManagerTest.class.getSimpleName();
+ private static final int TIMEOUT_MS = 5_000; // 5 seconds
+
private CarDevicePolicyManager mCarDpm;
private DevicePolicyManager mDpm;
private KeyguardManager mKeyguardManager;
@@ -110,9 +112,8 @@
// User is removed once switch is complete
Log.d(TAG, "waiting for user to be removed: " + user);
waitForUserRemoval(user.id);
- assertWithMessage("User %s exists after switch (should be removed)", user)
- .that(hasUser(user.id))
- .isFalse();
+ waitUntil("User " + user + " exists after switch (should be removed)",
+ TIMEOUT_MS, () -> !hasUser(user.id));
}
@Test
@@ -129,6 +130,64 @@
try {
assertWithMessage("Created user named %s and type %s: %s", name, type,
result).that(result.isSuccess()).isTrue();
+ assertWithMessage("%s is not an admin user", user).that(
+ mUserManager.isUserAdmin(user.getIdentifier())).isFalse();
+ assertWithMessage("Create user with name %s", name).that(
+ mUserManager.getUserInfo(user.getIdentifier()).name).isEqualTo(name);
+ } finally {
+ if (user != null) {
+ removeUser(user.getIdentifier());
+ }
+ }
+ }
+
+ @Test
+ public void testCreateAdminUser() throws Exception {
+ assertCanAddUser();
+
+ String name = "CarDevicePolicyManagerTest.testCreateUser";
+ int type = CarDevicePolicyManager.USER_TYPE_ADMIN;
+ Log.d(TAG, "creating new user with name " + name + " and type " + type);
+ CreateUserResult result = mCarDpm.createUser(name, type);
+ Log.d(TAG, "result: " + result);
+ UserHandle user = result.getUserHandle();
+
+ try {
+ assertWithMessage("Created user named %s and type %s: %s", name, type,
+ result).that(result.isSuccess()).isTrue();
+ assertWithMessage("%s is an admin user", user).that(
+ mUserManager.isUserAdmin(user.getIdentifier())).isTrue();
+ assertWithMessage("Create user with name %s", name).that(
+ mUserManager.getUserInfo(user.getIdentifier()).name).isEqualTo(name);
+
+ } finally {
+ if (user != null) {
+ removeUser(user.getIdentifier());
+ }
+ }
+ }
+
+ @Test
+ public void testCreateGuestUser() throws Exception {
+ assertCanAddUser();
+
+ String name = "CarDevicePolicyManagerTest.testCreateUser";
+ int type = CarDevicePolicyManager.USER_TYPE_GUEST;
+ Log.d(TAG, "creating new user with name " + name + " and type " + type);
+ CreateUserResult result = mCarDpm.createUser(name, type);
+ Log.d(TAG, "result: " + result);
+ UserHandle user = result.getUserHandle();
+
+ try {
+ assertWithMessage("Created user named %s and type %s: %s", name, type,
+ result).that(result.isSuccess()).isTrue();
+ assertWithMessage("%s is a guest user", user).that(
+ mUserManager.isGuestUser(user.getIdentifier())).isTrue();
+ assertWithMessage("%s is not an admin user", user).that(
+ mUserManager.isUserAdmin(user.getIdentifier())).isFalse();
+ assertWithMessage("Create user with name %s", name).that(
+ mUserManager.getUserInfo(user.getIdentifier()).name).isEqualTo(name);
+
} finally {
if (user != null) {
removeUser(user.getIdentifier());
@@ -145,14 +204,8 @@
StartUserInBackgroundResult result = mCarDpm.startUserInBackground(user.getUserHandle());
Log.d(TAG, "result: " + result);
- try {
- assertWithMessage("Result of startUserInBackground %s: %s", user.toFullString(), result)
- .that(result.isSuccess()).isTrue();
- } finally {
- // Clean up the created user.
- removeUser(user.id);
- waitForUserRemoval(user.id);
- }
+ assertWithMessage("Result of startUserInBackground %s: %s", user.toFullString(), result)
+ .that(result.isSuccess()).isTrue();
}
@Test
@@ -164,14 +217,8 @@
StopUserResult result = mCarDpm.stopUser(user.getUserHandle());
Log.d(TAG, "result: " + result);
- try {
- assertWithMessage("Result of stopUser %s: %s", user.toFullString(), result)
- .that(result.isSuccess()).isTrue();
- } finally {
- // Clean up the user.
- removeUser(user.id);
- waitForUserRemoval(user.id);
- }
+ assertWithMessage("Result of stopUser %s: %s", user.toFullString(), result)
+ .that(result.isSuccess()).isTrue();
}
@Test
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java b/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
index 05f1208..db79dc4 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarMultiUserTestBase.java
@@ -191,7 +191,7 @@
Log.i(TAG, "removing users at end of " + getTestName() + ": " + mUsersToRemove);
for (Integer userId : mUsersToRemove) {
if (hasUser(userId)) {
- removeUser(userId);
+ mUserManager.removeUser(userId);
}
}
} else {
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyManagerTest.java
index 88bf883..97b45cb 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyManagerTest.java
@@ -124,18 +124,19 @@
}
Log.d(TAG, "Stopping car service for test");
mTestManager.stopCarService(mToken);
- CarApiTestBase.executeShellCommand(
- "dumpsys android.hardware.automotive.vehicle.IVehicle/default "
- + "--genTestVendorConfigs");
+ String result = CarApiTestBase.executeShellCommand(
+ "dumpsys car_service gen-test-vendor-configs");
Log.d(TAG, "Starting car service for test");
mTestManager.startCarService(mToken);
List<CarPropertyConfig> carPropertyConfigsList = mCarPropertyManager.getPropertyList();
-
resultSet = new ArraySet<>();
for (int i = 0; i < carPropertyConfigsList.size(); i++) {
resultSet.add(carPropertyConfigsList.get(i).getPropertyId());
}
+ assumeTrue("successfully generated vendor configs, VHAL gen-test-vendor-configs "
+ + "debug command is supported",
+ result.equals("successfully generated vendor configs\n"));
for (int i = STARTING_TEST_CODES; i < END_TEST_CODES; i++) {
assertThat(i).isIn(resultSet);
}
@@ -151,11 +152,13 @@
try {
Log.d(TAG, "Stopping car service for test");
mTestManager.stopCarService(mToken);
- CarApiTestBase.executeShellCommand(
- "dumpsys android.hardware.automotive.vehicle.IVehicle/default "
- + "--genTestVendorConfigs");
+ String result = CarApiTestBase.executeShellCommand(
+ "dumpsys car_service gen-test-vendor-configs");
Log.d(TAG, "Starting car service for test");
mTestManager.startCarService(mToken);
+ assumeTrue("successfully generated vendor configs, VHAL gen-test-vendor-configs "
+ + "debug command is supported",
+ result.equals("successfully generated vendor configs\n"));
Executor callbackExecutor = new HandlerExecutor(mHandler);
Set<Integer> setPropertyIds = new ArraySet<>();
List<CarPropertyManager.SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>();
@@ -191,11 +194,13 @@
try {
Log.d(TAG, "Stopping car service for test");
mTestManager.stopCarService(mToken);
- CarApiTestBase.executeShellCommand(
- "dumpsys android.hardware.automotive.vehicle.IVehicle/default "
- + "--genTestVendorConfigs");
+ String result = CarApiTestBase.executeShellCommand(
+ "dumpsys car_service gen-test-vendor-configs");
Log.d(TAG, "Starting car service for test");
mTestManager.startCarService(mToken);
+ assumeTrue("successfully generated vendor configs, VHAL gen-test-vendor-configs "
+ + "debug command is supported",
+ result.equals("successfully generated vendor configs\n"));
Executor callbackExecutor = new HandlerExecutor(mHandler);
Set<Integer> getPropertyIds = new ArraySet<>();
@@ -227,9 +232,9 @@
private void restoreCarService() throws Exception {
Log.d(TAG, "Stopping car service for test");
mTestManager.stopCarService(mToken);
- CarApiTestBase.executeShellCommand(
- "dumpsys android.hardware.automotive.vehicle.IVehicle/default "
- + "--restoreVendorConfigs");
+ String result = CarApiTestBase.executeShellCommand(
+ "dumpsys car_service restore-vendor-configs");
+ assertThat(result.equals("successfully restored vendor configs\n")).isTrue();
Log.d(TAG, "Starting car service for test");
mTestManager.startCarService(mToken);
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
index 0c42711..8563e37 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
@@ -18,8 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertThrows;
-
import android.car.VehicleAreaType;
import android.car.hardware.CarPropertyValue;
import android.test.suitebuilder.annotation.MediumTest;
@@ -105,6 +103,14 @@
}
@Test
+ public void equals_returnsFalseForDifferentStatuses() {
+ int differentStatus = CarPropertyValue.STATUS_ERROR;
+ assertThat(CAR_PROPERTY_VALUE.equals(
+ new CarPropertyValue<>(PROPERTY_ID, AREA_ID, differentStatus, TIMESTAMP_NANOS,
+ VALUE))).isFalse();
+ }
+
+ @Test
public void equals_returnsFalseForDifferentTimestamps() {
long differentTimestampNanos = 76845;
assertThat(CAR_PROPERTY_VALUE.equals(
@@ -143,15 +149,13 @@
}
@Test
- public void carPropertyvalue_getStatus() {
+ public void getStatus_returnsAvailable() {
assertThat(CAR_PROPERTY_VALUE.getStatus()).isEqualTo(CarPropertyValue.STATUS_AVAILABLE);
}
@Test
- public void carPropertyValue_notAvailableStatus() {
- assertThrows(IllegalArgumentException.class, () -> new CarPropertyValue<>(
- PROPERTY_ID, AREA_ID, CarPropertyValue.STATUS_UNAVAILABLE, TIMESTAMP_NANOS, null));
- assertThrows(IllegalArgumentException.class, () -> new CarPropertyValue<>(
- PROPERTY_ID, AREA_ID, CarPropertyValue.STATUS_ERROR, TIMESTAMP_NANOS, null));
+ public void getStatus_returnsError() {
+ assertThat(new CarPropertyValue<>(PROPERTY_ID, AREA_ID, CarPropertyValue.STATUS_ERROR,
+ TIMESTAMP_NANOS, VALUE).getStatus()).isEqualTo(CarPropertyValue.STATUS_ERROR);
}
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerLifecycleEventFilterTest.java b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerLifecycleEventFilterTest.java
index 0a0fe33..c8cde2a 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarUserManagerLifecycleEventFilterTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarUserManagerLifecycleEventFilterTest.java
@@ -178,10 +178,10 @@
mCarUserManager.removeListener(listener.listener);
}
+ // We do not need to clean up the user that is created by
+ // CarMultiUserTestBase#createUser().
// TODO(b/246959046): Listen to the user removed event after investigating why some
// other events, e,g. stopping, stopped and removed are out of order.
- // Remove the new user for cleanup.
- removeUser(newUserId);
// The expected events are (in order): CREATED, STARTING, SWITCHING, SWITCHING.
UserLifecycleEvent[] events = buildExpectedEvents(initialUserId, newUserId);
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_using_oem_defined_context.xml b/tests/carservice_test/res/raw/car_audio_configuration_using_oem_defined_context.xml
new file mode 100644
index 0000000..9ba506a
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_using_oem_defined_context.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" ?>
+<!-- Copyright (C) 2023 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.
+-->
+<carAudioConfiguration version="3">
+ <oemContexts>
+ <oemContext name="OEM_MUSIC">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_UNKNOWN" />
+ <usage value="AUDIO_USAGE_MEDIA" />
+ <usage value="AUDIO_USAGE_GAME" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_NAVIGATION">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_VOICE_COMMAND">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+ <usage value="AUDIO_USAGE_ASSISTANT" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_CALL_RING">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_CALL">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+ <usage value="AUDIO_USAGE_CALL_ASSISTANT" />
+ <usage value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_ALARM">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ALARM" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_NOTIFICATION">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_NOTIFICATION" />
+ <usage value="AUDIO_USAGE_NOTIFICATION_EVENT" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_SYSTEM_SOUND">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_EMERGENCY">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_EMERGENCY" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_SAFETY">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_SAFETY" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_VEHICLE_STATUS">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_VEHICLE_STATUS" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_ANNOUNCEMENT">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ANNOUNCEMENT" />
+ </audioAttributes>
+ </oemContext>
+ </oemContexts>
+ <zones>
+ <zone isPrimary="true" name="primary zone" audioZoneId="0" occupantZoneId="0">
+ <zoneConfigs>
+ <zoneConfig name="primary zone config 1" isDefault="true">
+ <volumeGroups>
+ <group name="OEM_VOLUME_GROUP">
+ <device address="bus0_media_out">
+ <context context="OEM_MUSIC"/>
+ <context context="OEM_NAVIGATION"/>
+ <context context="OEM_VOICE_COMMAND"/>
+ <context context="OEM_CALL_RING"/>
+ <context context="OEM_CALL"/>
+ <context context="OEM_ALARM"/>
+ <context context="OEM_NOTIFICATION"/>
+ <context context="OEM_SYSTEM_SOUND"/>
+ <context context="OEM_EMERGENCY"/>
+ <context context="OEM_SAFETY"/>
+ <context context="OEM_VEHICLE_STATUS"/>
+ <context context="OEM_ANNOUNCEMENT"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zoneConfig>
+ </zoneConfigs>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java b/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
index 736c5ae..0f6aa40 100644
--- a/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarEvsManagerTest.java
@@ -27,6 +27,7 @@
import android.car.evs.CarEvsStatus;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
@@ -63,6 +64,7 @@
private static final int SMALL_NAP_MS = 500;
private static final int ACTIVITY_REQUEST_TIMEOUT_SEC = 3;
private static final int STREAM_REQUEST_TIMEOUT_SEC = 1;
+ private static final int STREAM_EVENT_TIMEOUT_SEC = 2;
// Will return frame buffers in the order they arrived.
private static final int INDEX_TO_FIRST_ELEM = 0;
@@ -70,8 +72,7 @@
private final ArrayList<CarEvsBufferDescriptor> mReceivedBuffers = new ArrayList<>();
private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
private final Semaphore mFrameReceivedSignal = new Semaphore(0);
- private final Semaphore mServiceInRequestedState = new Semaphore(0);
- private final Semaphore mServiceInActiveState = new Semaphore(0);
+ private final Semaphore mStreamEventOccurred = new Semaphore(0);
private final Car mCar = Car.createCar(ApplicationProvider.getApplicationContext());
private final CarEvsManager mEvsManager =
@@ -79,6 +80,8 @@
private final EvsStreamCallbackImpl mStreamCallback = new EvsStreamCallbackImpl();
private final EvsStatusListenerImpl mStatusListener = new EvsStatusListenerImpl();
+ private @CarEvsStreamEvent int mLastStreamEvent;
+
@Before
public void setUp() {
assumeTrue(mCar.isFeatureEnabled(Car.CAR_EVS_SERVICE));
@@ -89,8 +92,7 @@
// Drains all permits
mFrameReceivedSignal.drainPermits();
- mServiceInRequestedState.drainPermits();
- mServiceInActiveState.drainPermits();
+ mStreamEventOccurred.drainPermits();
// Ensures no stream is active
mEvsManager.stopVideoStream();
@@ -109,33 +111,6 @@
}
@Test
- public void testSetStatusListener() throws Exception {
- // Registers a status listener and start monitoring the CarEvsService's state changes.
- mEvsManager.setStatusListener(mCallbackExecutor, mStatusListener);
-
- // Requests to start the rearview activity.
- assertThat(
- mEvsManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW)
- ).isEqualTo(CarEvsManager.ERROR_NONE);
-
- // Waits until the CarEvsService enters the REQUESTED state.
- assertThat(
- mServiceInRequestedState.tryAcquire(ACTIVITY_REQUEST_TIMEOUT_SEC, TimeUnit.SECONDS)
- ).isTrue();
-
- // Waits until the CarEvsService starts a video stream; it enters the ACTIVE state.
- assertThat(
- mServiceInActiveState.tryAcquire(STREAM_REQUEST_TIMEOUT_SEC, TimeUnit.SECONDS)
- ).isTrue();
-
- // Requests to stop the rearview activity.
- mEvsManager.stopActivity();
-
- // Unregisters a status listener.
- mEvsManager.clearStatusListener();
- }
-
- @Test
public void testStartAndStopVideoStream() throws Exception {
// Registers a status listener and start monitoring the CarEvsService's state changes.
mEvsManager.setStatusListener(mCallbackExecutor, mStatusListener);
@@ -146,12 +121,7 @@
/* token = */ null, mCallbackExecutor, mStreamCallback)
).isEqualTo(CarEvsManager.ERROR_NONE);
- // Waits until the service starts the video stream.
- assertThat(
- mServiceInActiveState.tryAcquire(STREAM_REQUEST_TIMEOUT_SEC, TimeUnit.SECONDS)
- ).isTrue();
-
- // Then, waits for a few frames frame buffers
+ // Waits for a few frame buffers
for (int i = 0; i < NUMBER_OF_FRAMES_TO_WAIT; ++i) {
assertThat(
mFrameReceivedSignal.tryAcquire(FRAME_TIMEOUT_MS, TimeUnit.MILLISECONDS)
@@ -175,9 +145,8 @@
// Checks a current status a few hundreds milliseconds after. CarEvsService will move into
// the inactive state when it gets a stream-stopped event from the EVS manager.
SystemClock.sleep(SMALL_NAP_MS);
- status = mEvsManager.getCurrentStatus();
- assertThat(status).isNotNull();
- assertThat(status.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ assertThat(mStreamCallback.waitForStreamEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
// Unregister a listener
mEvsManager.clearStatusListener();
@@ -198,23 +167,7 @@
private final class EvsStatusListenerImpl implements CarEvsManager.CarEvsStatusListener {
@Override
public void onStatusChanged(CarEvsStatus status) {
- switch (status.getState()) {
- case CarEvsManager.SERVICE_STATE_REQUESTED:
- mServiceInRequestedState.release();
- break;
-
- case CarEvsManager.SERVICE_STATE_ACTIVE:
- mServiceInActiveState.release();
- break;
-
- case CarEvsManager.SERVICE_STATE_UNAVAILABLE:
- // Nothing to do
- break;
-
- default:
- // Nothing to do
- break;
- }
+ Log.i(TAG, "Received a notification of status changed to " + status.getState());
}
}
@@ -225,19 +178,8 @@
private final class EvsStreamCallbackImpl implements CarEvsManager.CarEvsStreamCallback {
@Override
public void onStreamEvent(@CarEvsStreamEvent int event) {
- switch(event) {
- case CarEvsManager.STREAM_EVENT_STREAM_STARTED:
- // Ignores this event for now because our reference EVS HAL does not send this
- // event.
- break;
-
- case CarEvsManager.STREAM_EVENT_STREAM_STOPPED:
- break;
-
- default:
- // Ignores other stream events in this test.
- break;
- }
+ mLastStreamEvent = event;
+ mStreamEventOccurred.release();
}
@Override
@@ -248,5 +190,22 @@
// Notifies a new frame's arrival
mFrameReceivedSignal.release();
}
+
+ public boolean waitForStreamEvent(@CarEvsStreamEvent int expected) {
+ while (mLastStreamEvent != expected) {
+ try {
+ if (!mStreamEventOccurred.tryAcquire(STREAM_EVENT_TIMEOUT_SEC,
+ TimeUnit.SECONDS)) {
+ Log.e(TAG, "No stream event is received before the timer expired.");
+ return false;
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Current waiting thread is interrupted. ", e);
+ return false;
+ }
+ }
+
+ return true;
+ }
}
}
diff --git a/tests/carservice_test/src/com/android/car/CarPropertyManagerTest.java b/tests/carservice_test/src/com/android/car/CarPropertyManagerTest.java
index 70e991a..6d73094 100644
--- a/tests/carservice_test/src/com/android/car/CarPropertyManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarPropertyManagerTest.java
@@ -266,10 +266,14 @@
}
private void setUpTargetSdk() {
- // Default will use R
- getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
+ getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
if (mTestName.getMethodName().endsWith("BeforeR")) {
getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q;
+ } else if (mTestName.getMethodName().endsWith("BeforeS")
+ || mTestName.getMethodName().endsWith("AfterR")) {
+ getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
+ } else if (mTestName.getMethodName().endsWith("AfterS")) {
+ getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
}
}
@@ -359,63 +363,62 @@
/**
* Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
- * error status before R.
+ * error status before S.
*/
@Test
- public void testGetIntArrayPropertyWithErrorStatusBeforeR() {
+ public void testGetIntArrayPropertyWithErrorStatusBeforeS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isLessThan(Build.VERSION_CODES.R);
- assertThrows(IllegalStateException.class,
- () -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, 0));
+ .isLessThan(Build.VERSION_CODES.S);
+ assertThat(mManager.getIntArrayProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, 0)).isEqualTo(
+ new int[0]);
}
/**
* Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
- * error status equal or after R.
+ * error status equal or after S.
*/
@Test
- public void testGetIntArrayPropertyWithErrorStatusEqualAfterR() {
+ public void testGetIntArrayPropertyWithErrorStatusEqualAfterS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isAtLeast(Build.VERSION_CODES.R);
+ .isAtLeast(Build.VERSION_CODES.S);
assertThrows(CarInternalErrorException.class,
() -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, 0));
}
/**
* Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
- * unknown status before R.
+ * unknown status before S.
*/
@Test
- public void testGetIntArrayPropertyWithUnknownStatusBeforeR() {
+ public void testGetIntArrayPropertyWithUnknownStatusBeforeS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isLessThan(Build.VERSION_CODES.R);
- assertThrows(IllegalStateException.class,
- () -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, 0));
+ .isLessThan(Build.VERSION_CODES.S);
+ assertThat(mManager.getIntArrayProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, 0)).isEqualTo(
+ new int[0]);
}
/**
* Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
- * unknown status equal or after R.
+ * unknown status equal or after S.
*/
@Test
- public void testGetIntArrayPropertyWithUnknownStatusEqualAfterR() {
+ public void testGetIntArrayPropertyWithUnknownStatusEqualAfterS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isAtLeast(Build.VERSION_CODES.R);
+ .isAtLeast(Build.VERSION_CODES.S);
assertThrows(CarInternalErrorException.class,
() -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, 0));
}
/**
* Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with
- * unavailable status before R.
+ * unavailable status before S.
*/
@Test
- public void testGetIntPropertyWithUnavailableStatusBeforeR() {
+ public void testGetIntPropertyWithUnavailableStatusBeforeS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isLessThan(Build.VERSION_CODES.R);
- assertThrows(IllegalStateException.class,
- () -> mManager.getIntProperty(PROP_VALUE_STATUS_UNAVAILABLE_INT, 0));
-
+ .isLessThan(Build.VERSION_CODES.S);
+ assertThat(
+ mManager.getIntProperty(PROP_VALUE_STATUS_UNAVAILABLE_INT, 0)).isEqualTo(0);
}
/**
@@ -423,7 +426,7 @@
* unavailable status equal or after R.
*/
@Test
- public void testGetIntPropertyWithUnavailableStatusEqualAfterR() {
+ public void testGetIntPropertyWithUnavailableStatusEqualAfterS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
.isAtLeast(Build.VERSION_CODES.R);
assertThrows(PropertyNotAvailableException.class,
@@ -435,13 +438,11 @@
* error status before R.
*/
@Test
- public void testGetBooleanPropertyWithErrorStatusBeforeR() {
+ public void testGetBooleanPropertyWithErrorStatusBeforeS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isLessThan(Build.VERSION_CODES.R);
- mManager.setProperty(Boolean.class, PROP_VALUE_STATUS_ERROR_BOOLEAN,
- 0, FAKE_BOOLEAN_PROPERTY_VALUE);
- assertThrows(IllegalStateException.class,
- () -> mManager.getBooleanProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, 0));
+ .isLessThan(Build.VERSION_CODES.S);
+ assertThat(mManager.getBooleanProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, 0)).isEqualTo(
+ false);
}
/**
@@ -449,27 +450,22 @@
* error status equal or after R.
*/
@Test
- public void testGetBooleanPropertyWithErrorStatusEqualAfterR() {
+ public void testGetBooleanPropertyWithErrorStatusEqualAfterS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
.isAtLeast(Build.VERSION_CODES.R);
- mManager.setProperty(Boolean.class, PROP_VALUE_STATUS_ERROR_BOOLEAN,
- 0, FAKE_BOOLEAN_PROPERTY_VALUE);
assertThrows(CarInternalErrorException.class,
() -> mManager.getBooleanProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, 0));
}
/**
* Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with
- * unavailable status before R.
+ * unavailable status before S.
*/
@Test
- public void testGetFloatPropertyWithUnavailableStatusBeforeR() {
+ public void testGetFloatPropertyWithUnavailableStatusBeforeS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
- .isLessThan(Build.VERSION_CODES.R);
- mManager.setProperty(Float.class, PROP_VALUE_STATUS_UNAVAILABLE_FLOAT,
- 0, FAKE_FLOAT_PROPERTY_VALUE);
- assertThrows(IllegalStateException.class,
- () -> mManager.getFloatProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, 0));
+ .isLessThan(Build.VERSION_CODES.S);
+ assertThat(mManager.getFloatProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, 0)).isEqualTo(0f);
}
/**
@@ -477,11 +473,9 @@
* unavailable status equal or after R.
*/
@Test
- public void testGetFloatPropertyWithUnavailableStatusEqualAfterR() {
+ public void testGetFloatPropertyWithUnavailableStatusEqualAfterS() {
Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
.isAtLeast(Build.VERSION_CODES.R);
- mManager.setProperty(Float.class, PROP_VALUE_STATUS_UNAVAILABLE_FLOAT,
- 0, FAKE_FLOAT_PROPERTY_VALUE);
assertThrows(PropertyNotAvailableException.class,
() -> mManager.getFloatProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, 0));
}
@@ -576,23 +570,76 @@
}
@Test
- public void testRegisterPropertyGetInitialValueErrorFromVhal() throws Exception {
- for (int propId : List.of(
- PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
- PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
- PROP_CAUSE_STATUS_CODE_INVALID_ARG,
- PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
- PROP_CAUSE_STATUS_CODE_TRY_AGAIN)) {
- TestCallback callback = new TestCallback(/* initValueCount= */ 1,
- /* changeEventCount= */ 0, /* errorEventCount= */ 0);
+ public void testRegisterPropertyGetInitialValueHandleNotAvailableStatusCode() throws Exception {
+ TestCallback callback = new TestCallback(/* initValueCount= */ 1,
+ /* changeEventCount= */ 0, /* errorEventCount= */ 0);
- mManager.registerCallback(callback, propId,
- CarPropertyManager.SENSOR_RATE_ONCHANGE);
+ mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
+ CarPropertyManager.SENSOR_RATE_ONCHANGE);
- // We should not receive any initial value event.
- assertThrows(IllegalStateException.class, () -> callback.assertRegisterCompleted(
- /* timeoutInMs=*/ 1000));
- }
+ callback.assertRegisterCompleted();
+ List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
+ assertThat(carPropertyValues).hasSize(1);
+ assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
+ CarPropertyValue.STATUS_UNAVAILABLE);
+ }
+
+ @Test
+ public void testRegisterPropertyGetInitialValueHandleAccessDeniedStatusCodes()
+ throws Exception {
+ TestCallback callback = new TestCallback(/* initValueCount= */ 1,
+ /* changeEventCount= */ 0, /* errorEventCount= */ 0);
+
+ mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
+ CarPropertyManager.SENSOR_RATE_ONCHANGE);
+
+ callback.assertRegisterCompleted();
+ List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
+ assertThat(carPropertyValues).hasSize(1);
+ assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
+ CarPropertyValue.STATUS_ERROR);
+ }
+
+ @Test
+ public void testRegisterPropertyGetInitialValueHandleInternalErrorStatusCodes()
+ throws Exception {
+ TestCallback callback = new TestCallback(/* initValueCount= */ 1,
+ /* changeEventCount= */ 0, /* errorEventCount= */ 0);
+
+ mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
+ CarPropertyManager.SENSOR_RATE_ONCHANGE);
+
+ callback.assertRegisterCompleted();
+ List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
+ assertThat(carPropertyValues).hasSize(1);
+ assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
+ CarPropertyValue.STATUS_ERROR);
+ }
+
+ @Test
+ public void testRegisterPropertyGetInitialValueHandleInvalidArgStatusCode() throws Exception {
+ TestCallback callback = new TestCallback(/* initValueCount= */ 1,
+ /* changeEventCount= */ 0, /* errorEventCount= */ 0);
+
+ mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
+ CarPropertyManager.SENSOR_RATE_ONCHANGE);
+
+ // We should not receive any initial value event.
+ assertThrows(IllegalStateException.class, () -> callback.assertRegisterCompleted(
+ /* timeoutInMs=*/ 1000));
+ }
+
+ @Test
+ public void testRegisterPropertyGetInitialValueHandleTryAgainStatusCode() throws Exception {
+ TestCallback callback = new TestCallback(/* initValueCount= */ 1,
+ /* changeEventCount= */ 0, /* errorEventCount= */ 0);
+
+ mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
+ CarPropertyManager.SENSOR_RATE_ONCHANGE);
+
+ // We should not receive any initial value event.
+ assertThrows(IllegalStateException.class, () -> callback.assertRegisterCompleted(
+ /* timeoutInMs=*/ 1000));
}
@Test
@@ -771,9 +818,7 @@
@Test
public void testOnChangeEventWithSameAreaId() throws Exception {
// init
- mManager.setProperty(Integer.class,
- CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
- TestCallback callback = new TestCallback(/* initValueCount= */ 1, /* changeEventCount= */ 1,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
/* errorEventCount= */ 0);
mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
callback.assertRegisterCompleted();
@@ -806,9 +851,7 @@
@Test
public void testOnChangeEventWithDifferentAreaId() throws Exception {
// init
- mManager.setProperty(Integer.class,
- CUSTOM_SEAT_INT_PROP_2, DRIVER_SIDE_AREA_ID, 1);
- TestCallback callback = new TestCallback(/* initValueCount= */ 1, /* changeEventCount= */ 2,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 2,
/* errorEventCount= */ 0);
mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0);
callback.assertRegisterCompleted();
@@ -839,10 +882,9 @@
}
@Test
- public void testOnChangeEventPropErrorStatusIgnored() throws Exception {
+ public void testOnChangeEventPropErrorStatus() throws Exception {
// init
- mManager.setProperty(Integer.class, CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
- TestCallback callback = new TestCallback(/* initValueCount= */ 1, /* changeEventCount= */ 1,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
/* errorEventCount= */ 0);
mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
@@ -858,14 +900,15 @@
getAidlMockedVehicleHal().injectEvent(prop);
- assertThat(callback.getChangeEventCounter()).isEqualTo(0);
+ List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
+ assertThat(carPropertyValues).hasSize(1);
+ assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(CarPropertyValue.STATUS_ERROR);
}
@Test
public void testOnChangeEventPropUnavailableStatus() throws Exception {
// init
- mManager.setProperty(Integer.class, CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
- TestCallback callback = new TestCallback(/* initValueCount= */ 1, /* changeEventCount= */ 1,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
/* errorEventCount= */ 0);
mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
@@ -874,20 +917,23 @@
VehiclePropValue prop = new VehiclePropValue();
prop.prop = CUSTOM_SEAT_INT_PROP_1;
+ prop.areaId = DRIVER_SIDE_AREA_ID;
prop.value = new RawPropValues();
prop.status = VehiclePropertyStatus.UNAVAILABLE;
prop.timestamp = SystemClock.elapsedRealtimeNanos();
getAidlMockedVehicleHal().injectEvent(prop);
- assertThat(callback.getChangeEventCounter()).isEqualTo(0);
+ List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
+ assertThat(carPropertyValues).hasSize(1);
+ assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
+ CarPropertyValue.STATUS_UNAVAILABLE);
}
@Test
public void testOnChangeEventInvalidPayload() throws Exception {
// init
- mManager.setProperty(Integer.class, CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
- TestCallback callback = new TestCallback(/* initValueCount= */ 1, /* changeEventCount= */ 0,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 0,
/* errorEventCount= */ 0);
mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
callback.assertRegisterCompleted();
@@ -934,31 +980,36 @@
float newEnoughWheelLeftFrontValue = 33.33f;
long newEnoughWheelLeftFrontTimestampNanos = Duration.ofSeconds(2).toNanos();
- TestCallback callback = new TestCallback(/* initValueCount= */ 0, /* changeEventCount= */ 2,
+ TestCallback callback = new TestCallback(/* initValueCount= */ 4, /* changeEventCount= */ 2,
/* errorEventCount= */ 0);
assertThat(mManager.registerCallback(callback, VehiclePropertyIds.TIRE_PRESSURE,
1f)).isTrue();
+ callback.assertRegisterCompleted();
+ long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
- wheelLeftFrontValue, wheelLeftFrontTimestampNanos));
+ wheelLeftFrontValue,
+ currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos));
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
- notNewEnoughWheelLeftFrontValue, notNewEnoughWheelLeftFrontTimestampNanos));
+ notNewEnoughWheelLeftFrontValue,
+ currentElapsedRealtimeNanos + notNewEnoughWheelLeftFrontTimestampNanos));
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
- newEnoughWheelLeftFrontValue, newEnoughWheelLeftFrontTimestampNanos));
+ newEnoughWheelLeftFrontValue,
+ currentElapsedRealtimeNanos + newEnoughWheelLeftFrontTimestampNanos));
List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
assertThat(carPropertyValues).hasSize(2);
assertTirePressureCarPropertyValue(carPropertyValues.get(0),
VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
- wheelLeftFrontTimestampNanos);
+ currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos);
assertTirePressureCarPropertyValue(carPropertyValues.get(1),
VehicleAreaWheel.WHEEL_LEFT_FRONT, newEnoughWheelLeftFrontValue,
- newEnoughWheelLeftFrontTimestampNanos);
+ currentElapsedRealtimeNanos + newEnoughWheelLeftFrontTimestampNanos);
}
@Test
@@ -976,42 +1027,54 @@
float wheelRightRearValue = 44.44f;
long wheelRightRearTimestampNanos = Duration.ofSeconds(1).toNanos();
- TestCallback callback = new TestCallback(/* initValueCount= */ 0, /* changeEventCount= */ 4,
+ // Initially we have 4 area Ids, so we will have 4 initial values. In the test we will
+ // inject 4 events which will generate 4 change events.
+ TestCallback callback = new TestCallback(/* initValueCount= */ 4, /* changeEventCount= */ 4,
/* errorEventCount= */ 0);
+ // AidlMockedVehicleHal will not actually genenerate property events for continuous
+ // property, so the subscription rate does not matter.
assertThat(mManager.registerCallback(callback, VehiclePropertyIds.TIRE_PRESSURE, 1f))
.isTrue();
+ callback.assertRegisterCompleted();
+
+ long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
// inject events in time order from newest to oldest
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
- wheelLeftFrontValue, wheelLeftFrontTimestampNanos));
+ wheelLeftFrontValue,
+ currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos));
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_FRONT,
- wheelRightFrontValue, wheelRightFrontTimestampNanos));
+ wheelRightFrontValue,
+ currentElapsedRealtimeNanos + wheelRightFrontTimestampNanos));
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_REAR,
- wheelLeftRearValue, wheelLeftRearTimestampNanos));
+ wheelLeftRearValue,
+ currentElapsedRealtimeNanos + wheelLeftRearTimestampNanos));
getAidlMockedVehicleHal().injectEvent(
newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_REAR,
- wheelRightRearValue, wheelRightRearTimestampNanos));
+ wheelRightRearValue,
+ currentElapsedRealtimeNanos + wheelRightRearTimestampNanos));
List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
assertThat(carPropertyValues).hasSize(4);
assertTirePressureCarPropertyValue(carPropertyValues.get(0),
VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
- wheelLeftFrontTimestampNanos);
+ currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos);
assertTirePressureCarPropertyValue(carPropertyValues.get(1),
VehicleAreaWheel.WHEEL_RIGHT_FRONT, wheelRightFrontValue,
- wheelRightFrontTimestampNanos);
+ currentElapsedRealtimeNanos + wheelRightFrontTimestampNanos);
assertTirePressureCarPropertyValue(carPropertyValues.get(2),
- VehicleAreaWheel.WHEEL_LEFT_REAR, wheelLeftRearValue, wheelLeftRearTimestampNanos);
+ VehicleAreaWheel.WHEEL_LEFT_REAR, wheelLeftRearValue,
+ currentElapsedRealtimeNanos + wheelLeftRearTimestampNanos);
assertTirePressureCarPropertyValue(carPropertyValues.get(3),
VehicleAreaWheel.WHEEL_RIGHT_REAR, wheelRightRearValue,
- wheelRightRearTimestampNanos);
+ currentElapsedRealtimeNanos + wheelRightRearTimestampNanos);
}
@Test
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index c81eaf8..72f48a0 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -354,7 +354,9 @@
Log.i(TAG, "tearDown");
// Wait for CPMS to finish event processing.
- waitUntilPowerStateChangeHandled();
+ if (mCarImpl != null) {
+ waitUntilPowerStateChangeHandled();
+ }
try {
if (mCar != null) {
diff --git a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
index aa81b0e..7626165 100644
--- a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
+++ b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
@@ -70,6 +70,77 @@
public class CarAudioZonesHelperTest extends AbstractExtendedMockitoTestCase {
private static final String TAG = CarAudioZonesHelperTest.class.getSimpleName();
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_MUSIC =
+ new CarAudioContextInfo(new AudioAttributes[] {
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_UNKNOWN),
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_GAME),
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_MEDIA)
+ }, "OEM_MUSIC", 1);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_NAVIGATION =
+ new CarAudioContextInfo(new AudioAttributes[] {
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes
+ .USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)}, "OEM_NAVIGATION", 2);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_VOICE_COMMAND =
+ new CarAudioContextInfo(new AudioAttributes[] {
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY),
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_ASSISTANT)
+ }, "OEM_VOICE_COMMAND", 3);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_CALL_RING =
+ new CarAudioContextInfo(new AudioAttributes[] {
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE)}, "OEM_CALL_RING", 4);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_CALL =
+ new CarAudioContextInfo(new AudioAttributes[] {
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_VOICE_COMMUNICATION),
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_CALL_ASSISTANT),
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes
+ .USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ }, "OEM_CALL", 5);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_ALARM =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_ALARM)
+ }, "OEM_ALARM", 6);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_NOTIFICATION =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_NOTIFICATION),
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_NOTIFICATION_EVENT)}, "OEM_NOTIFICATION", 7);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_SYSTEM_SOUND =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)},
+ "OEM_SYSTEM_SOUND", 8);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_EMERGENCY =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_EMERGENCY)
+ }, "OEM_EMERGENCY", 9);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_SAFETY =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_SAFETY)
+ }, "OEM_SAFETY", 10);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_VEHICLE_STATUS =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(
+ AudioAttributes.USAGE_VEHICLE_STATUS)}, "OEM_VEHICLE_STATUS", 11);
+
+ private static final CarAudioContextInfo OEM_CONTEXT_INFO_ANNOUNCEMENT =
+ new CarAudioContextInfo(new AudioAttributes[]{
+ CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_ANNOUNCEMENT)
+ }, "OEM_ANNOUNCEMENT", 12);
+
public static final CarAudioContext TEST_CAR_AUDIO_CONTEXT =
new CarAudioContext(CarAudioContext.getAllContextsInfo(),
/* useCoreAudioRouting= */ false);
@@ -517,6 +588,29 @@
}
@Test
+ public void getCarAudioContext_withOEMContexts() throws Exception {
+ try (InputStream oemDefinedContextStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_using_oem_defined_context)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mAudioManager, mCarAudioSettings,
+ oemDefinedContextStream, mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos,
+ /* useCarVolumeGroupMute= */ false, /* useCoreAudioVolume= */ false,
+ /* useCoreAudioRouting= */ false);
+ cazh.loadAudioZones();
+
+ CarAudioContext contexts = cazh.getCarAudioContext();
+
+ assertWithMessage("OEM defined contexts")
+ .that(contexts.getContextsInfo()).containsExactly(OEM_CONTEXT_INFO_MUSIC,
+ OEM_CONTEXT_INFO_NAVIGATION, OEM_CONTEXT_INFO_VOICE_COMMAND,
+ OEM_CONTEXT_INFO_CALL_RING, OEM_CONTEXT_INFO_CALL,
+ OEM_CONTEXT_INFO_ALARM, OEM_CONTEXT_INFO_NOTIFICATION,
+ OEM_CONTEXT_INFO_SYSTEM_SOUND, OEM_CONTEXT_INFO_EMERGENCY,
+ OEM_CONTEXT_INFO_SAFETY, OEM_CONTEXT_INFO_VEHICLE_STATUS,
+ OEM_CONTEXT_INFO_ANNOUNCEMENT);
+ }
+ }
+
+ @Test
public void loadAudioZones_versionTwoParsesContexts() throws Exception {
try (InputStream versionTwoStream = mContext.getResources().openRawResource(
R.raw.car_audio_configuration_V2)) {
diff --git a/tests/carservice_unit_test/AndroidManifest.xml b/tests/carservice_unit_test/AndroidManifest.xml
index 66815ef..13ac1fd 100644
--- a/tests/carservice_unit_test/AndroidManifest.xml
+++ b/tests/carservice_unit_test/AndroidManifest.xml
@@ -36,6 +36,7 @@
<application android:label="@string/app_title"
android:debuggable="true" android:testOnly="true">
<uses-library android:name="android.test.runner" />
+ <activity android:name="android.car.app.CarTaskViewControllerHostLifecycleFactoryTest$TestActivity"/>
<activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityA"/>
<activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityB"/>
<activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityC"/>
diff --git a/tests/carservice_unit_test/res/raw/car_addedinorbefore_apis.txt b/tests/carservice_unit_test/res/raw/car_addedinorbefore_apis.txt
deleted file mode 100644
index 1a7228c..0000000
--- a/tests/carservice_unit_test/res/raw/car_addedinorbefore_apis.txt
+++ /dev/null
@@ -1,2814 +0,0 @@
-android.car.AoapService.RESULT_OK
-android.car.AoapService.RESULT_DEVICE_NOT_SUPPORTED
-android.car.AoapService.RESULT_DO_NOT_SWITCH_TO_AOAP
-android.car.AoapService.MSG_NEW_DEVICE_ATTACHED
-android.car.AoapService.MSG_NEW_DEVICE_ATTACHED_RESPONSE
-android.car.AoapService.MSG_CAN_SWITCH_TO_AOAP
-android.car.AoapService.MSG_CAN_SWITCH_TO_AOAP_RESPONSE
-android.car.AoapService.KEY_DEVICE
-android.car.AoapService.KEY_RESULT
-android.car.AoapService.isDeviceSupported
-android.car.AoapService.canSwitchToAoap
-android.car.AoapService.onCreate
-android.car.AoapService.onBind
-android.car.AoapService.onUnbind
-android.car.AoapService.dump
-android.car.ApiVersion.isAtLeast
-android.car.ApiVersion.getMajorVersion
-android.car.ApiVersion.getMinorVersion
-android.car.ApiVersion.equals
-android.car.ApiVersion.hashCode
-android.car.ApiVersion.toString
-android.car.ApiVersion.writeToParcel
-android.car.ApiVersion.readFromParcel
-android.car.Car.CAR_SERVICE_BINDER_SERVICE_NAME
-android.car.Car.META_DATA_DISTRACTION_OPTIMIZED
-android.car.Car.META_DATA_REQUIRES_CAR_FEATURE
-android.car.Car.SENSOR_SERVICE
-android.car.Car.INFO_SERVICE
-android.car.Car.APP_FOCUS_SERVICE
-android.car.Car.PACKAGE_SERVICE
-android.car.Car.AUDIO_SERVICE
-android.car.Car.CAR_NAVIGATION_SERVICE
-android.car.Car.CAR_OCCUPANT_ZONE_SERVICE
-android.car.Car.CAR_USER_SERVICE
-android.car.Car.EXPERIMENTAL_CAR_USER_SERVICE
-android.car.Car.CAR_DEVICE_POLICY_SERVICE
-android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE
-android.car.Car.CABIN_SERVICE
-android.car.Car.DIAGNOSTIC_SERVICE
-android.car.Car.HVAC_SERVICE
-android.car.Car.POWER_SERVICE
-android.car.Car.PROJECTION_SERVICE
-android.car.Car.PROPERTY_SERVICE
-android.car.Car.VENDOR_EXTENSION_SERVICE
-android.car.Car.VEHICLE_MAP_SERVICE
-android.car.Car.VMS_SUBSCRIBER_SERVICE
-android.car.Car.CAR_DRIVING_STATE_SERVICE
-android.car.Car.CAR_UX_RESTRICTION_SERVICE
-android.car.Car.OCCUPANT_AWARENESS_SERVICE
-android.car.Car.CAR_MEDIA_SERVICE
-android.car.Car.CAR_BUGREPORT_SERVICE
-android.car.Car.STORAGE_MONITORING_SERVICE
-android.car.Car.CAR_WATCHDOG_SERVICE
-android.car.Car.CAR_PERFORMANCE_SERVICE
-android.car.Car.CAR_INPUT_SERVICE
-android.car.Car.CLUSTER_HOME_SERVICE
-android.car.Car.TEST_SERVICE
-android.car.Car.CAR_EVS_SERVICE
-android.car.Car.CAR_TELEMETRY_SERVICE
-android.car.Car.CAR_ACTIVITY_SERVICE
-android.car.Car.PERMISSION_MILEAGE
-android.car.Car.PERMISSION_ENERGY
-android.car.Car.PERMISSION_CONTROL_CAR_ENERGY
-android.car.Car.PERMISSION_ADJUST_RANGE_REMAINING
-android.car.Car.PERMISSION_IDENTIFICATION
-android.car.Car.PERMISSION_SPEED
-android.car.Car.PERMISSION_CAR_DYNAMICS_STATE
-android.car.Car.PERMISSION_ENERGY_PORTS
-android.car.Car.PERMISSION_CONTROL_ENERGY_PORTS
-android.car.Car.PERMISSION_EXTERIOR_LIGHTS
-android.car.Car.PERMISSION_READ_INTERIOR_LIGHTS
-android.car.Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS
-android.car.Car.PERMISSION_CONTROL_INTERIOR_LIGHTS
-android.car.Car.PERMISSION_POWERTRAIN
-android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME
-android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS
-android.car.Car.PERMISSION_RECEIVE_CAR_AUDIO_DUCKING_EVENTS
-android.car.Car.PERMISSION_CAR_NAVIGATION_MANAGER
-android.car.Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL
-android.car.Car.PERMISSION_CAR_MONITOR_CLUSTER_NAVIGATION_STATE
-android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER
-android.car.Car.PERMISSION_CAR_INFO
-android.car.Car.PERMISSION_PRIVILEGED_CAR_INFO
-android.car.Car.PERMISSION_READ_CAR_VENDOR_PERMISSION_INFO
-android.car.Car.PERMISSION_EXTERIOR_ENVIRONMENT
-android.car.Car.PERMISSION_VENDOR_EXTENSION
-android.car.Car.PERMISSION_CONTROL_APP_BLOCKING
-android.car.Car.PERMISSION_CAR_ENGINE_DETAILED
-android.car.Car.PERMISSION_TIRES
-android.car.Car.PERMISSION_CAR_EPOCH_TIME
-android.car.Car.PERMISSION_READ_STEERING_STATE
-android.car.Car.PERMISSION_READ_DISPLAY_UNITS
-android.car.Car.PERMISSION_CONTROL_DISPLAY_UNITS
-android.car.Car.PERMISSION_CONTROL_CAR_DOORS
-android.car.Car.PERMISSION_CONTROL_CAR_WINDOWS
-android.car.Car.PERMISSION_CONTROL_CAR_SEATS
-android.car.Car.PERMISSION_CONTROL_CAR_MIRRORS
-android.car.Car.PERMISSION_CONTROL_CAR_CLIMATE
-android.car.Car.PERMISSION_CAR_POWER
-android.car.Car.PERMISSION_READ_CAR_POWER_POLICY
-android.car.Car.PERMISSION_CONTROL_CAR_POWER_POLICY
-android.car.Car.PERMISSION_CONTROL_SHUTDOWN_PROCESS
-android.car.Car.PERMISSION_CAR_PROJECTION
-android.car.Car.PERMISSION_CAR_PROJECTION_STATUS
-android.car.Car.PERMISSION_MOCK_VEHICLE_HAL
-android.car.Car.PERMISSION_CAR_TEST_SERVICE
-android.car.Car.PERMISSION_CAR_DRIVING_STATE
-android.car.Car.PERMISSION_BIND_VMS_CLIENT
-android.car.Car.PERMISSION_VMS_PUBLISHER
-android.car.Car.PERMISSION_VMS_SUBSCRIBER
-android.car.Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL
-android.car.Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR
-android.car.Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION
-android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE
-android.car.Car.ACCESS_PRIVATE_DISPLAY_ID
-android.car.Car.PERMISSION_CONTROL_CAR_OCCUPANT_AWARENESS_SYSTEM
-android.car.Car.PERMISSION_STORAGE_MONITORING
-android.car.Car.PERMISSION_CONTROL_CAR_FEATURES
-android.car.Car.PERMISSION_USE_CAR_WATCHDOG
-android.car.Car.PERMISSION_CAR_MONITOR_INPUT
-android.car.Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY
-android.car.Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY
-android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA
-android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS
-android.car.Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE
-android.car.Car.CONNECTION_TYPE_EMBEDDED
-android.car.Car.PERMISSION_TEMPLATE_RENDERER
-android.car.Car.PERMISSION_CONTROL_CAR_WATCHDOG_CONFIG
-android.car.Car.PERMISSION_COLLECT_CAR_WATCHDOG_METRICS
-android.car.Car.PERMISSION_COLLECT_CAR_CPU_INFO
-android.car.Car.PERMISSION_CONTROL_CAR_APP_LAUNCH
-android.car.Car.CAR_TEMPLATE_HOST_RENDERER_SERVICE
-android.car.Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE
-android.car.Car.CAR_EXTRA_MEDIA_COMPONENT
-android.car.Car.CAR_EXTRA_MEDIA_PACKAGE
-android.car.Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION
-android.car.Car.CAR_SERVICE_INTERFACE_NAME
-android.car.Car.CAR_CATEGORY_NAVIGATION
-android.car.Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE
-android.car.Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER
-android.car.Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT
-android.car.Car.FEATURE_REQUEST_SUCCESS
-android.car.Car.FEATURE_REQUEST_ALREADY_IN_THE_STATE
-android.car.Car.FEATURE_REQUEST_MANDATORY
-android.car.Car.FEATURE_REQUEST_NOT_EXISTING
-android.car.Car.getCarVersion
-android.car.Car.getPlatformVersion
-android.car.Car.isApiVersionAtLeast
-android.car.Car.isApiVersionAtLeast
-android.car.Car.isApiAndPlatformVersionAtLeast
-android.car.Car.isApiAndPlatformVersionAtLeast
-android.car.Car.createCar
-android.car.Car.createCar
-android.car.Car.createCar
-android.car.Car.createCar
-android.car.Car.createCar
-android.car.Car.connect
-android.car.Car.disconnect
-android.car.Car.isConnected
-android.car.Car.isConnecting
-android.car.Car.getServiceConnectionListener
-android.car.Car.getCarManager
-android.car.Car.getCarManager
-android.car.Car.getCarConnectionType
-android.car.Car.isFeatureEnabled
-android.car.Car.enableFeature
-android.car.Car.disableFeature
-android.car.Car.getAllEnabledFeatures
-android.car.Car.getAllPendingDisabledFeatures
-android.car.Car.getAllPendingEnabledFeatures
-android.car.Car.getContext
-android.car.Car.getEventHandler
-android.car.Car.handleRemoteExceptionFromCarService
-android.car.Car.handleRemoteExceptionFromCarService
-android.car.Car.handleRemoteExceptionFromCarService
-android.car.Car.handleRemoteExceptionFromCarService
-android.car.Car.CarServiceLifecycleListener.onLifecycleChanged
-android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
-android.car.CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND
-android.car.CarAppFocusManager.APP_FOCUS_MAX
-android.car.CarAppFocusManager.APP_FOCUS_REQUEST_FAILED
-android.car.CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED
-android.car.CarAppFocusManager.addFocusListener
-android.car.CarAppFocusManager.removeFocusListener
-android.car.CarAppFocusManager.removeFocusListener
-android.car.CarAppFocusManager.getActiveAppTypes
-android.car.CarAppFocusManager.getAppTypeOwner
-android.car.CarAppFocusManager.isOwningFocus
-android.car.CarAppFocusManager.requestAppFocus
-android.car.CarAppFocusManager.abandonAppFocus
-android.car.CarAppFocusManager.abandonAppFocus
-android.car.CarAppFocusManager.onCarDisconnected
-android.car.CarAppFocusManager.OnAppFocusChangedListener.onAppFocusChanged
-android.car.CarAppFocusManager.OnAppFocusOwnershipCallback.onAppFocusOwnershipLost
-android.car.CarAppFocusManager.OnAppFocusOwnershipCallback.onAppFocusOwnershipGranted
-android.car.CarBugreportManager.requestBugreport
-android.car.CarBugreportManager.requestBugreportForTesting
-android.car.CarBugreportManager.cancelBugreport
-android.car.CarBugreportManager.onCarDisconnected
-android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_FAILED
-android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_IN_PROGRESS
-android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED
-android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_SERVICE_NOT_AVAILABLE
-android.car.CarBugreportManager.CarBugreportManagerCallback.onProgress
-android.car.CarBugreportManager.CarBugreportManagerCallback.onError
-android.car.CarBugreportManager.CarBugreportManagerCallback.onFinished
-android.car.CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE
-android.car.CarInfoManager.BASIC_INFO_KEY_MANUFACTURER
-android.car.CarInfoManager.BASIC_INFO_KEY_MODEL
-android.car.CarInfoManager.BASIC_INFO_KEY_MODEL_YEAR
-android.car.CarInfoManager.BASIC_INFO_KEY_VEHICLE_ID
-android.car.CarInfoManager.INFO_KEY_PRODUCT_CONFIGURATION
-android.car.CarInfoManager.BASIC_INFO_DRIVER_SEAT
-android.car.CarInfoManager.BASIC_INFO_EV_PORT_LOCATION
-android.car.CarInfoManager.BASIC_INFO_FUEL_DOOR_LOCATION
-android.car.CarInfoManager.BASIC_INFO_FUEL_CAPACITY
-android.car.CarInfoManager.BASIC_INFO_FUEL_TYPES
-android.car.CarInfoManager.BASIC_INFO_EV_BATTERY_CAPACITY
-android.car.CarInfoManager.BASIC_INFO_EV_CONNECTOR_TYPES
-android.car.CarInfoManager.getManufacturer
-android.car.CarInfoManager.getModel
-android.car.CarInfoManager.getModelYear
-android.car.CarInfoManager.getModelYearInInteger
-android.car.CarInfoManager.getVehicleId
-android.car.CarInfoManager.getFuelCapacity
-android.car.CarInfoManager.getFuelTypes
-android.car.CarInfoManager.getEvBatteryCapacity
-android.car.CarInfoManager.getEvConnectorTypes
-android.car.CarInfoManager.getDriverSeat
-android.car.CarInfoManager.getEvPortLocation
-android.car.CarInfoManager.getFuelDoorLocation
-android.car.CarInfoManager.onCarDisconnected
-android.car.CarLibLog.TAG_CAR
-android.car.CarLibLog.TAG_CLUSTER
-android.car.CarLibLog.TAG_INPUT
-android.car.CarLibLog.TAG_NAV
-android.car.CarLibLog.TAG_SENSOR
-android.car.CarLibLog.TAG_DIAGNOSTIC
-android.car.CarManagerBase.mCar
-android.car.CarManagerBase.getContext
-android.car.CarManagerBase.getEventHandler
-android.car.CarManagerBase.handleRemoteExceptionFromCarService
-android.car.CarManagerBase.handleRemoteExceptionFromCarService
-android.car.CarManagerBase.handleExceptionFromCarService
-android.car.CarManagerBase.onCarDisconnected
-android.car.CarManagerBase.addDumpable
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_MAIN
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_HUD
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_INPUT
-android.car.CarOccupantZoneManager.DISPLAY_TYPE_AUXILIARY
-android.car.CarOccupantZoneManager.OCCUPANT_TYPE_INVALID
-android.car.CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER
-android.car.CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER
-android.car.CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER
-android.car.CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY
-android.car.CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER
-android.car.CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_AUDIO
-android.car.CarOccupantZoneManager.getAllOccupantZones
-android.car.CarOccupantZoneManager.getAllDisplaysForOccupant
-android.car.CarOccupantZoneManager.getDisplayForOccupant
-android.car.CarOccupantZoneManager.getDisplayIdForDriver
-android.car.CarOccupantZoneManager.getAudioZoneIdForOccupant
-android.car.CarOccupantZoneManager.getOccupantForAudioZoneId
-android.car.CarOccupantZoneManager.getDisplayType
-android.car.CarOccupantZoneManager.getUserForOccupant
-android.car.CarOccupantZoneManager.getUserForDisplayId
-android.car.CarOccupantZoneManager.assignProfileUserToOccupantZone
-android.car.CarOccupantZoneManager.assignVisibleUserToOccupantZone
-android.car.CarOccupantZoneManager.unassignOccupantZone
-android.car.CarOccupantZoneManager.registerOccupantZoneConfigChangeListener
-android.car.CarOccupantZoneManager.unregisterOccupantZoneConfigChangeListener
-android.car.CarOccupantZoneManager.getMyOccupantZone
-android.car.CarOccupantZoneManager.getOccupantZoneForUser
-android.car.CarOccupantZoneManager.getOccupantZone
-android.car.CarOccupantZoneManager.hasDriverZone
-android.car.CarOccupantZoneManager.hasPassengerZones
-android.car.CarOccupantZoneManager.onCarDisconnected
-android.car.CarOccupantZoneManager.getSupportedInputTypes
-android.car.CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID
-android.car.CarOccupantZoneManager.OccupantZoneInfo.zoneId
-android.car.CarOccupantZoneManager.OccupantZoneInfo.occupantType
-android.car.CarOccupantZoneManager.OccupantZoneInfo.seat
-android.car.CarOccupantZoneManager.OccupantZoneInfo.CREATOR
-android.car.CarOccupantZoneManager.OccupantZoneInfo.describeContents
-android.car.CarOccupantZoneManager.OccupantZoneInfo.writeToParcel
-android.car.CarOccupantZoneManager.OccupantZoneInfo.equals
-android.car.CarOccupantZoneManager.OccupantZoneInfo.hashCode
-android.car.CarOccupantZoneManager.OccupantZoneInfo.toString
-android.car.CarOccupantZoneManager.OccupantZoneConfigChangeListener.onOccupantZoneConfigChanged
-android.car.CarProjectionManager.PROJECTION_VOICE_SEARCH
-android.car.CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH
-android.car.CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN
-android.car.CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP
-android.car.CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN
-android.car.CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP
-android.car.CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN
-android.car.CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP
-android.car.CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN
-android.car.CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP
-android.car.CarProjectionManager.NUM_KEY_EVENTS
-android.car.CarProjectionManager.PROJECTION_AP_STARTED
-android.car.CarProjectionManager.PROJECTION_AP_STOPPED
-android.car.CarProjectionManager.PROJECTION_AP_FAILED
-android.car.CarProjectionManager.regsiterProjectionListener
-android.car.CarProjectionManager.registerProjectionListener
-android.car.CarProjectionManager.unregsiterProjectionListener
-android.car.CarProjectionManager.unregisterProjectionListener
-android.car.CarProjectionManager.addKeyEventHandler
-android.car.CarProjectionManager.addKeyEventHandler
-android.car.CarProjectionManager.removeKeyEventHandler
-android.car.CarProjectionManager.registerProjectionRunner
-android.car.CarProjectionManager.unregisterProjectionRunner
-android.car.CarProjectionManager.onCarDisconnected
-android.car.CarProjectionManager.startProjectionAccessPoint
-android.car.CarProjectionManager.getAvailableWifiChannels
-android.car.CarProjectionManager.stopProjectionAccessPoint
-android.car.CarProjectionManager.requestBluetoothProfileInhibit
-android.car.CarProjectionManager.releaseBluetoothProfileInhibit
-android.car.CarProjectionManager.updateProjectionStatus
-android.car.CarProjectionManager.registerProjectionStatusListener
-android.car.CarProjectionManager.unregisterProjectionStatusListener
-android.car.CarProjectionManager.getProjectionOptions
-android.car.CarProjectionManager.resetProjectionAccessPointCredentials
-android.car.CarProjectionManager.CarProjectionListener.onVoiceAssistantRequest
-android.car.CarProjectionManager.ProjectionKeyEventHandler.onKeyEvent
-android.car.CarProjectionManager.ProjectionStatusListener.onProjectionStatusChanged
-android.car.CarProjectionManager.ProjectionAccessPointCallback.ERROR_NO_CHANNEL
-android.car.CarProjectionManager.ProjectionAccessPointCallback.ERROR_GENERIC
-android.car.CarProjectionManager.ProjectionAccessPointCallback.ERROR_INCOMPATIBLE_MODE
-android.car.CarProjectionManager.ProjectionAccessPointCallback.ERROR_TETHERING_DISALLOWED
-android.car.CarProjectionManager.ProjectionAccessPointCallback.onStarted
-android.car.CarProjectionManager.ProjectionAccessPointCallback.onStarted
-android.car.CarProjectionManager.ProjectionAccessPointCallback.onStopped
-android.car.CarProjectionManager.ProjectionAccessPointCallback.onFailed
-android.car.CarVersion.forMajorAndMinorVersions
-android.car.CarVersion.forMajorVersion
-android.car.CarVersion.describeContents
-android.car.CarVersion.writeToParcel
-android.car.EvConnectorType.UNKNOWN
-android.car.EvConnectorType.J1772
-android.car.EvConnectorType.MENNEKES
-android.car.EvConnectorType.CHADEMO
-android.car.EvConnectorType.COMBO_1
-android.car.EvConnectorType.COMBO_2
-android.car.EvConnectorType.TESLA_ROADSTER
-android.car.EvConnectorType.TESLA_HPWC
-android.car.EvConnectorType.TESLA_SUPERCHARGER
-android.car.EvConnectorType.GBT
-android.car.EvConnectorType.GBT_DC
-android.car.EvConnectorType.SCAME
-android.car.EvConnectorType.OTHER
-android.car.FuelType.UNKNOWN
-android.car.FuelType.UNLEADED
-android.car.FuelType.LEADED
-android.car.FuelType.DIESEL_1
-android.car.FuelType.DIESEL_2
-android.car.FuelType.BIODIESEL
-android.car.FuelType.E85
-android.car.FuelType.LPG
-android.car.FuelType.CNG
-android.car.FuelType.LNG
-android.car.FuelType.ELECTRIC
-android.car.FuelType.HYDROGEN
-android.car.FuelType.OTHER
-android.car.PlatformVersion.forMajorAndMinorVersions
-android.car.PlatformVersion.forMajorVersion
-android.car.PlatformVersion.describeContents
-android.car.PlatformVersion.writeToParcel
-android.car.PlatformVersionMismatchException.getMessage
-android.car.PlatformVersionMismatchException.getMinimumPlatformApiVersion
-android.car.PlatformVersionMismatchException.writeToParcel
-android.car.PlatformVersionMismatchException.describeContents
-android.car.PortLocationType.UNKNOWN
-android.car.PortLocationType.FRONT_LEFT
-android.car.PortLocationType.FRONT_RIGHT
-android.car.PortLocationType.REAR_RIGHT
-android.car.PortLocationType.REAR_LEFT
-android.car.PortLocationType.FRONT
-android.car.PortLocationType.REAR
-android.car.VehicleAreaDoor.DOOR_ROW_1_LEFT
-android.car.VehicleAreaDoor.DOOR_ROW_1_RIGHT
-android.car.VehicleAreaDoor.DOOR_ROW_2_LEFT
-android.car.VehicleAreaDoor.DOOR_ROW_2_RIGHT
-android.car.VehicleAreaDoor.DOOR_ROW_3_LEFT
-android.car.VehicleAreaDoor.DOOR_ROW_3_RIGHT
-android.car.VehicleAreaDoor.DOOR_HOOD
-android.car.VehicleAreaDoor.DOOR_REAR
-android.car.VehicleAreaMirror.MIRROR_DRIVER_LEFT
-android.car.VehicleAreaMirror.MIRROR_DRIVER_RIGHT
-android.car.VehicleAreaMirror.MIRROR_DRIVER_CENTER
-android.car.VehicleAreaSeat.SEAT_UNKNOWN
-android.car.VehicleAreaSeat.SEAT_ROW_1_LEFT
-android.car.VehicleAreaSeat.SEAT_ROW_1_CENTER
-android.car.VehicleAreaSeat.SEAT_ROW_1_RIGHT
-android.car.VehicleAreaSeat.SEAT_ROW_2_LEFT
-android.car.VehicleAreaSeat.SEAT_ROW_2_CENTER
-android.car.VehicleAreaSeat.SEAT_ROW_2_RIGHT
-android.car.VehicleAreaSeat.SEAT_ROW_3_LEFT
-android.car.VehicleAreaSeat.SEAT_ROW_3_CENTER
-android.car.VehicleAreaSeat.SEAT_ROW_3_RIGHT
-android.car.VehicleAreaSeat.SIDE_LEFT
-android.car.VehicleAreaSeat.SIDE_CENTER
-android.car.VehicleAreaSeat.SIDE_RIGHT
-android.car.VehicleAreaSeat.fromRowAndSide
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_SEAT
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_DOOR
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR
-android.car.VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL
-android.car.VehicleAreaWheel.WHEEL_UNKNOWN
-android.car.VehicleAreaWheel.WHEEL_LEFT_FRONT
-android.car.VehicleAreaWheel.WHEEL_RIGHT_FRONT
-android.car.VehicleAreaWheel.WHEEL_LEFT_REAR
-android.car.VehicleAreaWheel.WHEEL_RIGHT_REAR
-android.car.VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD
-android.car.VehicleAreaWindow.WINDOW_REAR_WINDSHIELD
-android.car.VehicleAreaWindow.WINDOW_ROW_1_LEFT
-android.car.VehicleAreaWindow.WINDOW_ROW_1_RIGHT
-android.car.VehicleAreaWindow.WINDOW_ROW_2_LEFT
-android.car.VehicleAreaWindow.WINDOW_ROW_2_RIGHT
-android.car.VehicleAreaWindow.WINDOW_ROW_3_LEFT
-android.car.VehicleAreaWindow.WINDOW_ROW_3_RIGHT
-android.car.VehicleAreaWindow.WINDOW_ROOF_TOP_1
-android.car.VehicleAreaWindow.WINDOW_ROOF_TOP_2
-android.car.VehicleGear.GEAR_UNKNOWN
-android.car.VehicleGear.GEAR_NEUTRAL
-android.car.VehicleGear.GEAR_REVERSE
-android.car.VehicleGear.GEAR_PARK
-android.car.VehicleGear.GEAR_DRIVE
-android.car.VehicleGear.GEAR_FIRST
-android.car.VehicleGear.GEAR_SECOND
-android.car.VehicleGear.GEAR_THIRD
-android.car.VehicleGear.GEAR_FOURTH
-android.car.VehicleGear.GEAR_FIFTH
-android.car.VehicleGear.GEAR_SIXTH
-android.car.VehicleGear.GEAR_SEVENTH
-android.car.VehicleGear.GEAR_EIGHTH
-android.car.VehicleGear.GEAR_NINTH
-android.car.VehicleGear.toString
-android.car.VehicleHvacFanDirection.FACE
-android.car.VehicleHvacFanDirection.FLOOR
-android.car.VehicleHvacFanDirection.DEFROST
-android.car.VehicleIgnitionState.UNDEFINED
-android.car.VehicleIgnitionState.LOCK
-android.car.VehicleIgnitionState.OFF
-android.car.VehicleIgnitionState.ACC
-android.car.VehicleIgnitionState.ON
-android.car.VehicleIgnitionState.START
-android.car.VehicleIgnitionState.toString
-android.car.VehicleLightState.OFF
-android.car.VehicleLightState.ON
-android.car.VehicleLightState.DAYTIME_RUNNING
-android.car.VehicleLightSwitch.OFF
-android.car.VehicleLightSwitch.ON
-android.car.VehicleLightSwitch.DAYTIME_RUNNING
-android.car.VehicleLightSwitch.AUTOMATIC
-android.car.VehicleOilLevel.CRITICALLY_LOW
-android.car.VehicleOilLevel.LOW
-android.car.VehicleOilLevel.NORMAL
-android.car.VehicleOilLevel.HIGH
-android.car.VehicleOilLevel.ERROR
-android.car.VehiclePropertyAccess.NONE
-android.car.VehiclePropertyAccess.READ
-android.car.VehiclePropertyAccess.WRITE
-android.car.VehiclePropertyAccess.READ_WRITE
-android.car.VehiclePropertyIds.INVALID
-android.car.VehiclePropertyIds.INFO_VIN
-android.car.VehiclePropertyIds.INFO_MAKE
-android.car.VehiclePropertyIds.INFO_MODEL
-android.car.VehiclePropertyIds.INFO_MODEL_YEAR
-android.car.VehiclePropertyIds.INFO_FUEL_CAPACITY
-android.car.VehiclePropertyIds.INFO_FUEL_TYPE
-android.car.VehiclePropertyIds.INFO_EV_BATTERY_CAPACITY
-android.car.VehiclePropertyIds.INFO_EV_CONNECTOR_TYPE
-android.car.VehiclePropertyIds.INFO_FUEL_DOOR_LOCATION
-android.car.VehiclePropertyIds.INFO_EV_PORT_LOCATION
-android.car.VehiclePropertyIds.INFO_MULTI_EV_PORT_LOCATIONS
-android.car.VehiclePropertyIds.INFO_DRIVER_SEAT
-android.car.VehiclePropertyIds.INFO_EXTERIOR_DIMENSIONS
-android.car.VehiclePropertyIds.PERF_ODOMETER
-android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED
-android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY
-android.car.VehiclePropertyIds.PERF_STEERING_ANGLE
-android.car.VehiclePropertyIds.PERF_REAR_STEERING_ANGLE
-android.car.VehiclePropertyIds.ENGINE_COOLANT_TEMP
-android.car.VehiclePropertyIds.ENGINE_OIL_LEVEL
-android.car.VehiclePropertyIds.ENGINE_OIL_TEMP
-android.car.VehiclePropertyIds.ENGINE_RPM
-android.car.VehiclePropertyIds.WHEEL_TICK
-android.car.VehiclePropertyIds.FUEL_LEVEL
-android.car.VehiclePropertyIds.FUEL_DOOR_OPEN
-android.car.VehiclePropertyIds.EV_BATTERY_LEVEL
-android.car.VehiclePropertyIds.EV_CHARGE_PORT_OPEN
-android.car.VehiclePropertyIds.EV_CHARGE_PORT_CONNECTED
-android.car.VehiclePropertyIds.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE
-android.car.VehiclePropertyIds.RANGE_REMAINING
-android.car.VehiclePropertyIds.TIRE_PRESSURE
-android.car.VehiclePropertyIds.CRITICALLY_LOW_TIRE_PRESSURE
-android.car.VehiclePropertyIds.GEAR_SELECTION
-android.car.VehiclePropertyIds.CURRENT_GEAR
-android.car.VehiclePropertyIds.PARKING_BRAKE_ON
-android.car.VehiclePropertyIds.PARKING_BRAKE_AUTO_APPLY
-android.car.VehiclePropertyIds.FUEL_LEVEL_LOW
-android.car.VehiclePropertyIds.NIGHT_MODE
-android.car.VehiclePropertyIds.TURN_SIGNAL_STATE
-android.car.VehiclePropertyIds.IGNITION_STATE
-android.car.VehiclePropertyIds.ABS_ACTIVE
-android.car.VehiclePropertyIds.TRACTION_CONTROL_ACTIVE
-android.car.VehiclePropertyIds.HVAC_FAN_SPEED
-android.car.VehiclePropertyIds.HVAC_FAN_DIRECTION
-android.car.VehiclePropertyIds.HVAC_TEMPERATURE_CURRENT
-android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET
-android.car.VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION
-android.car.VehiclePropertyIds.HVAC_DEFROSTER
-android.car.VehiclePropertyIds.HVAC_AC_ON
-android.car.VehiclePropertyIds.HVAC_MAX_AC_ON
-android.car.VehiclePropertyIds.HVAC_MAX_DEFROST_ON
-android.car.VehiclePropertyIds.HVAC_RECIRC_ON
-android.car.VehiclePropertyIds.HVAC_DUAL_ON
-android.car.VehiclePropertyIds.HVAC_AUTO_ON
-android.car.VehiclePropertyIds.HVAC_SEAT_TEMPERATURE
-android.car.VehiclePropertyIds.HVAC_SIDE_MIRROR_HEAT
-android.car.VehiclePropertyIds.HVAC_STEERING_WHEEL_HEAT
-android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS
-android.car.VehiclePropertyIds.HVAC_ACTUAL_FAN_SPEED_RPM
-android.car.VehiclePropertyIds.HVAC_POWER_ON
-android.car.VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE
-android.car.VehiclePropertyIds.HVAC_AUTO_RECIRC_ON
-android.car.VehiclePropertyIds.HVAC_SEAT_VENTILATION
-android.car.VehiclePropertyIds.HVAC_ELECTRIC_DEFROSTER_ON
-android.car.VehiclePropertyIds.DISTANCE_DISPLAY_UNITS
-android.car.VehiclePropertyIds.FUEL_VOLUME_DISPLAY_UNITS
-android.car.VehiclePropertyIds.TIRE_PRESSURE_DISPLAY_UNITS
-android.car.VehiclePropertyIds.EV_BATTERY_DISPLAY_UNITS
-android.car.VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS
-android.car.VehiclePropertyIds.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME
-android.car.VehiclePropertyIds.ENV_OUTSIDE_TEMPERATURE
-android.car.VehiclePropertyIds.AP_POWER_STATE_REQ
-android.car.VehiclePropertyIds.AP_POWER_STATE_REPORT
-android.car.VehiclePropertyIds.AP_POWER_BOOTUP_REASON
-android.car.VehiclePropertyIds.DISPLAY_BRIGHTNESS
-android.car.VehiclePropertyIds.HW_KEY_INPUT
-android.car.VehiclePropertyIds.DOOR_POS
-android.car.VehiclePropertyIds.DOOR_MOVE
-android.car.VehiclePropertyIds.DOOR_LOCK
-android.car.VehiclePropertyIds.MIRROR_Z_POS
-android.car.VehiclePropertyIds.MIRROR_Z_MOVE
-android.car.VehiclePropertyIds.MIRROR_Y_POS
-android.car.VehiclePropertyIds.MIRROR_Y_MOVE
-android.car.VehiclePropertyIds.MIRROR_LOCK
-android.car.VehiclePropertyIds.MIRROR_FOLD
-android.car.VehiclePropertyIds.SEAT_MEMORY_SELECT
-android.car.VehiclePropertyIds.SEAT_MEMORY_SET
-android.car.VehiclePropertyIds.SEAT_BELT_BUCKLED
-android.car.VehiclePropertyIds.SEAT_BELT_HEIGHT_POS
-android.car.VehiclePropertyIds.SEAT_BELT_HEIGHT_MOVE
-android.car.VehiclePropertyIds.SEAT_FORE_AFT_POS
-android.car.VehiclePropertyIds.SEAT_FORE_AFT_MOVE
-android.car.VehiclePropertyIds.SEAT_BACKREST_ANGLE_1_POS
-android.car.VehiclePropertyIds.SEAT_BACKREST_ANGLE_1_MOVE
-android.car.VehiclePropertyIds.SEAT_BACKREST_ANGLE_2_POS
-android.car.VehiclePropertyIds.SEAT_BACKREST_ANGLE_2_MOVE
-android.car.VehiclePropertyIds.SEAT_HEIGHT_POS
-android.car.VehiclePropertyIds.SEAT_HEIGHT_MOVE
-android.car.VehiclePropertyIds.SEAT_DEPTH_POS
-android.car.VehiclePropertyIds.SEAT_DEPTH_MOVE
-android.car.VehiclePropertyIds.SEAT_TILT_POS
-android.car.VehiclePropertyIds.SEAT_TILT_MOVE
-android.car.VehiclePropertyIds.SEAT_LUMBAR_FORE_AFT_POS
-android.car.VehiclePropertyIds.SEAT_LUMBAR_FORE_AFT_MOVE
-android.car.VehiclePropertyIds.SEAT_LUMBAR_SIDE_SUPPORT_POS
-android.car.VehiclePropertyIds.SEAT_LUMBAR_SIDE_SUPPORT_MOVE
-android.car.VehiclePropertyIds.SEAT_HEADREST_HEIGHT_POS
-android.car.VehiclePropertyIds.SEAT_HEADREST_HEIGHT_MOVE
-android.car.VehiclePropertyIds.SEAT_HEADREST_ANGLE_POS
-android.car.VehiclePropertyIds.SEAT_HEADREST_ANGLE_MOVE
-android.car.VehiclePropertyIds.SEAT_HEADREST_FORE_AFT_POS
-android.car.VehiclePropertyIds.SEAT_HEADREST_FORE_AFT_MOVE
-android.car.VehiclePropertyIds.SEAT_OCCUPANCY
-android.car.VehiclePropertyIds.WINDOW_POS
-android.car.VehiclePropertyIds.WINDOW_MOVE
-android.car.VehiclePropertyIds.WINDOW_LOCK
-android.car.VehiclePropertyIds.VEHICLE_MAP_SERVICE
-android.car.VehiclePropertyIds.OBD2_LIVE_FRAME
-android.car.VehiclePropertyIds.OBD2_FREEZE_FRAME
-android.car.VehiclePropertyIds.OBD2_FREEZE_FRAME_INFO
-android.car.VehiclePropertyIds.OBD2_FREEZE_FRAME_CLEAR
-android.car.VehiclePropertyIds.HEADLIGHTS_STATE
-android.car.VehiclePropertyIds.HIGH_BEAM_LIGHTS_STATE
-android.car.VehiclePropertyIds.FOG_LIGHTS_STATE
-android.car.VehiclePropertyIds.HAZARD_LIGHTS_STATE
-android.car.VehiclePropertyIds.HEADLIGHTS_SWITCH
-android.car.VehiclePropertyIds.HIGH_BEAM_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.FOG_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.HAZARD_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.CABIN_LIGHTS_STATE
-android.car.VehiclePropertyIds.CABIN_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.READING_LIGHTS_STATE
-android.car.VehiclePropertyIds.READING_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.INITIAL_USER_INFO
-android.car.VehiclePropertyIds.SWITCH_USER
-android.car.VehiclePropertyIds.CREATE_USER
-android.car.VehiclePropertyIds.REMOVE_USER
-android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION
-android.car.VehiclePropertyIds.POWER_POLICY_REQ
-android.car.VehiclePropertyIds.POWER_POLICY_GROUP_REQ
-android.car.VehiclePropertyIds.CURRENT_POWER_POLICY
-android.car.VehiclePropertyIds.WATCHDOG_ALIVE
-android.car.VehiclePropertyIds.WATCHDOG_TERMINATED_PROCESS
-android.car.VehiclePropertyIds.VHAL_HEARTBEAT
-android.car.VehiclePropertyIds.CLUSTER_SWITCH_UI
-android.car.VehiclePropertyIds.CLUSTER_DISPLAY_STATE
-android.car.VehiclePropertyIds.CLUSTER_REPORT_STATE
-android.car.VehiclePropertyIds.CLUSTER_REQUEST_DISPLAY
-android.car.VehiclePropertyIds.CLUSTER_NAVIGATION_STATE
-android.car.VehiclePropertyIds.EPOCH_TIME
-android.car.VehiclePropertyIds.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE
-android.car.VehiclePropertyIds.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS
-android.car.VehiclePropertyIds.FRONT_FOG_LIGHTS_STATE
-android.car.VehiclePropertyIds.FRONT_FOG_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.REAR_FOG_LIGHTS_STATE
-android.car.VehiclePropertyIds.REAR_FOG_LIGHTS_SWITCH
-android.car.VehiclePropertyIds.EV_CHARGE_CURRENT_DRAW_LIMIT
-android.car.VehiclePropertyIds.EV_CHARGE_PERCENT_LIMIT
-android.car.VehiclePropertyIds.EV_CHARGE_STATE
-android.car.VehiclePropertyIds.EV_CHARGE_SWITCH
-android.car.VehiclePropertyIds.EV_CHARGE_TIME_REMAINING
-android.car.VehiclePropertyIds.EV_REGENERATIVE_BRAKING_STATE
-android.car.VehiclePropertyIds.VEHICLE_CURB_WEIGHT
-android.car.VehiclePropertyIds.TRAILER_PRESENT
-android.car.VehiclePropertyIds.toString
-android.car.VehiclePropertyType.STRING
-android.car.VehiclePropertyType.BOOLEAN
-android.car.VehiclePropertyType.INT32
-android.car.VehiclePropertyType.INT32_VEC
-android.car.VehiclePropertyType.INT64
-android.car.VehiclePropertyType.INT64_VEC
-android.car.VehiclePropertyType.FLOAT
-android.car.VehiclePropertyType.FLOAT_VEC
-android.car.VehiclePropertyType.BYTES
-android.car.VehiclePropertyType.MIXED
-android.car.VehiclePropertyType.MASK
-android.car.VehicleSeatOccupancyState.UNKNOWN
-android.car.VehicleSeatOccupancyState.VACANT
-android.car.VehicleSeatOccupancyState.OCCUPIED
-android.car.VehicleUnit.SHOULD_NOT_USE
-android.car.VehicleUnit.RPM
-android.car.VehicleUnit.HERTZ
-android.car.VehicleUnit.PERCENTILE
-android.car.VehicleUnit.NANO_SECS
-android.car.VehicleUnit.SECS
-android.car.VehicleUnit.YEAR
-android.car.VehicleUnit.MILLIAMPERE
-android.car.VehicleUnit.MILLIVOLT
-android.car.VehicleUnit.MILLIWATTS
-android.car.VehicleUnit.DEGREES
-android.car.VehicleUnit.MILLIMETER
-android.car.VehicleUnit.METER
-android.car.VehicleUnit.KILOMETER
-android.car.VehicleUnit.MILE
-android.car.VehicleUnit.CELSIUS
-android.car.VehicleUnit.FAHRENHEIT
-android.car.VehicleUnit.KELVIN
-android.car.VehicleUnit.MILLILITER
-android.car.VehicleUnit.LITER
-android.car.VehicleUnit.US_GALLON
-android.car.VehicleUnit.IMPERIAL_GALLON
-android.car.VehicleUnit.WATT_HOUR
-android.car.VehicleUnit.AMPERE_HOURS
-android.car.VehicleUnit.KILOWATT_HOUR
-android.car.VehicleUnit.KILOPASCAL
-android.car.VehicleUnit.PSI
-android.car.VehicleUnit.BAR
-android.car.VehicleUnit.METER_PER_SEC
-android.car.VehicleUnit.MILES_PER_HOUR
-android.car.VehicleUnit.KILOMETERS_PER_HOUR
-android.car.admin.CarDevicePolicyManager.TAG
-android.car.admin.CarDevicePolicyManager.USER_TYPE_REGULAR
-android.car.admin.CarDevicePolicyManager.USER_TYPE_ADMIN
-android.car.admin.CarDevicePolicyManager.USER_TYPE_GUEST
-android.car.admin.CarDevicePolicyManager.FIRST_USER_TYPE
-android.car.admin.CarDevicePolicyManager.LAST_USER_TYPE
-android.car.admin.CarDevicePolicyManager.removeUser
-android.car.admin.CarDevicePolicyManager.createUser
-android.car.admin.CarDevicePolicyManager.startUserInBackground
-android.car.admin.CarDevicePolicyManager.stopUser
-android.car.admin.CarDevicePolicyManager.setUserDisclaimerShown
-android.car.admin.CarDevicePolicyManager.setUserDisclaimerAcknowledged
-android.car.admin.CarDevicePolicyManager.onCarDisconnected
-android.car.admin.CreateUserResult.STATUS_SUCCESS
-android.car.admin.CreateUserResult.STATUS_FAILURE_INVALID_ARGUMENTS
-android.car.admin.CreateUserResult.STATUS_FAILURE_GENERIC
-android.car.admin.CreateUserResult.forGenericError
-android.car.admin.CreateUserResult.getStatus
-android.car.admin.CreateUserResult.isSuccess
-android.car.admin.CreateUserResult.getUserHandle
-android.car.admin.CreateUserResult.toString
-android.car.admin.RemoveUserResult.STATUS_SUCCESS
-android.car.admin.RemoveUserResult.STATUS_SUCCESS_LAST_ADMIN_REMOVED
-android.car.admin.RemoveUserResult.STATUS_SUCCESS_SET_EPHEMERAL
-android.car.admin.RemoveUserResult.STATUS_FAILURE_USER_DOES_NOT_EXIST
-android.car.admin.RemoveUserResult.STATUS_FAILURE_INVALID_ARGUMENTS
-android.car.admin.RemoveUserResult.STATUS_SUCCESS_LAST_ADMIN_SET_EPHEMERAL
-android.car.admin.RemoveUserResult.STATUS_FAILURE_GENERIC
-android.car.admin.RemoveUserResult.getStatus
-android.car.admin.RemoveUserResult.isSuccess
-android.car.admin.RemoveUserResult.toString
-android.car.admin.RemoveUserResult.statusToString
-android.car.admin.StartUserInBackgroundResult.STATUS_SUCCESS
-android.car.admin.StartUserInBackgroundResult.STATUS_SUCCESS_CURRENT_USER
-android.car.admin.StartUserInBackgroundResult.STATUS_FAILURE_USER_DOES_NOT_EXIST
-android.car.admin.StartUserInBackgroundResult.STATUS_FAILURE_GENERIC
-android.car.admin.StartUserInBackgroundResult.getStatus
-android.car.admin.StartUserInBackgroundResult.isSuccess
-android.car.admin.StartUserInBackgroundResult.toString
-android.car.admin.StopUserResult.STATUS_SUCCESS
-android.car.admin.StopUserResult.STATUS_FAILURE_CURRENT_USER
-android.car.admin.StopUserResult.STATUS_FAILURE_SYSTEM_USER
-android.car.admin.StopUserResult.STATUS_FAILURE_USER_DOES_NOT_EXIST
-android.car.admin.StopUserResult.STATUS_FAILURE_GENERIC
-android.car.admin.StopUserResult.getStatus
-android.car.admin.StopUserResult.isSuccess
-android.car.admin.StopUserResult.toString
-android.car.app.CarActivityManager.RESULT_SUCCESS
-android.car.app.CarActivityManager.RESULT_FAILURE
-android.car.app.CarActivityManager.RESULT_INVALID_USER
-android.car.app.CarActivityManager.ERROR_CODE_ACTIVITY_NOT_FOUND
-android.car.app.CarActivityManager.setPersistentActivity
-android.car.app.CarActivityManager.onCarDisconnected
-android.car.app.CarActivityManager.registerTaskMonitor
-android.car.app.CarActivityManager.onTaskAppeared
-android.car.app.CarActivityManager.onTaskVanished
-android.car.app.CarActivityManager.onTaskInfoChanged
-android.car.app.CarActivityManager.unregisterTaskMonitor
-android.car.app.CarActivityManager.getVisibleTasks
-android.car.app.CarActivityManager.startUserPickerOnDisplay
-android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION
-android.car.cluster.CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE
-android.car.cluster.CarInstrumentClusterManager.startActivity
-android.car.cluster.CarInstrumentClusterManager.registerCallback
-android.car.cluster.CarInstrumentClusterManager.unregisterCallback
-android.car.cluster.CarInstrumentClusterManager.onCarDisconnected
-android.car.cluster.CarInstrumentClusterManager.Callback.onClusterActivityStateChanged
-android.car.cluster.ClusterActivityState.isVisible
-android.car.cluster.ClusterActivityState.getUnobscuredBounds
-android.car.cluster.ClusterActivityState.getExtras
-android.car.cluster.ClusterActivityState.setVisible
-android.car.cluster.ClusterActivityState.setUnobscuredBounds
-android.car.cluster.ClusterActivityState.setExtras
-android.car.cluster.ClusterActivityState.create
-android.car.cluster.ClusterActivityState.fromBundle
-android.car.cluster.ClusterActivityState.toBundle
-android.car.cluster.ClusterActivityState.toString
-android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_NONE
-android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_HOME
-android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_ON_OFF
-android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_BOUNDS
-android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_INSETS
-android.car.cluster.ClusterHomeManager.CONFIG_UI_TYPE
-android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_ID
-android.car.cluster.ClusterHomeManager.registerClusterStateListener
-android.car.cluster.ClusterHomeManager.registerClusterNavigationStateListener
-android.car.cluster.ClusterHomeManager.unregisterClusterStateListener
-android.car.cluster.ClusterHomeManager.unregisterClusterNavigationStateListener
-android.car.cluster.ClusterHomeManager.reportState
-android.car.cluster.ClusterHomeManager.requestDisplay
-android.car.cluster.ClusterHomeManager.getClusterState
-android.car.cluster.ClusterHomeManager.startFixedActivityModeAsUser
-android.car.cluster.ClusterHomeManager.stopFixedActivityMode
-android.car.cluster.ClusterHomeManager.onCarDisconnected
-android.car.cluster.ClusterHomeManager.ClusterStateListener.onClusterStateChanged
-android.car.cluster.ClusterHomeManager.ClusterNavigationStateListener.onNavigationState
-android.car.cluster.renderer.InstrumentClusterRenderer.onCreate
-android.car.cluster.renderer.InstrumentClusterRenderer.onStart
-android.car.cluster.renderer.InstrumentClusterRenderer.onStop
-android.car.cluster.renderer.InstrumentClusterRenderer.createNavigationRenderer
-android.car.cluster.renderer.InstrumentClusterRenderer.getNavigationRenderer
-android.car.cluster.renderer.InstrumentClusterRenderer.initialize
-android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER
-android.car.cluster.renderer.InstrumentClusterRenderingService.onBind
-android.car.cluster.renderer.InstrumentClusterRenderingService.getNavigationRenderer
-android.car.cluster.renderer.InstrumentClusterRenderingService.onKeyEvent
-android.car.cluster.renderer.InstrumentClusterRenderingService.onNavigationComponentLaunched
-android.car.cluster.renderer.InstrumentClusterRenderingService.onNavigationComponentReleased
-android.car.cluster.renderer.InstrumentClusterRenderingService.startFixedActivityModeForDisplayAndUser
-android.car.cluster.renderer.InstrumentClusterRenderingService.stopFixedActivityMode
-android.car.cluster.renderer.InstrumentClusterRenderingService.getComponentFromPackage
-android.car.cluster.renderer.InstrumentClusterRenderingService.startNavigationActivity
-android.car.cluster.renderer.InstrumentClusterRenderingService.setClusterActivityLaunchOptions
-android.car.cluster.renderer.InstrumentClusterRenderingService.setClusterActivityLaunchOptions
-android.car.cluster.renderer.InstrumentClusterRenderingService.setClusterActivityState
-android.car.cluster.renderer.InstrumentClusterRenderingService.setClusterActivityState
-android.car.cluster.renderer.InstrumentClusterRenderingService.dump
-android.car.cluster.renderer.InstrumentClusterRenderingService.getBitmap
-android.car.cluster.renderer.InstrumentClusterRenderingService.getBitmap
-android.car.cluster.renderer.InstrumentClusterRenderingService.getBitmap
-android.car.cluster.renderer.NavigationRenderer.getNavigationProperties
-android.car.cluster.renderer.NavigationRenderer.onEvent
-android.car.cluster.renderer.NavigationRenderer.onNavigationStateChanged
-android.car.content.pm.AppBlockingPackageInfo.packageName
-android.car.content.pm.AppBlockingPackageInfo.FLAG_SYSTEM_APP
-android.car.content.pm.AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY
-android.car.content.pm.AppBlockingPackageInfo.flags
-android.car.content.pm.AppBlockingPackageInfo.minRevisionCode
-android.car.content.pm.AppBlockingPackageInfo.maxRevisionCode
-android.car.content.pm.AppBlockingPackageInfo.signatures
-android.car.content.pm.AppBlockingPackageInfo.activities
-android.car.content.pm.AppBlockingPackageInfo.CREATOR
-android.car.content.pm.AppBlockingPackageInfo.describeContents
-android.car.content.pm.AppBlockingPackageInfo.writeToParcel
-android.car.content.pm.AppBlockingPackageInfo.verify
-android.car.content.pm.AppBlockingPackageInfo.isActivityCovered
-android.car.content.pm.AppBlockingPackageInfo.hashCode
-android.car.content.pm.AppBlockingPackageInfo.equals
-android.car.content.pm.AppBlockingPackageInfo.toString
-android.car.content.pm.CarAppBlockingPolicy.whitelists
-android.car.content.pm.CarAppBlockingPolicy.blacklists
-android.car.content.pm.CarAppBlockingPolicy.CREATOR
-android.car.content.pm.CarAppBlockingPolicy.describeContents
-android.car.content.pm.CarAppBlockingPolicy.writeToParcel
-android.car.content.pm.CarAppBlockingPolicy.hashCode
-android.car.content.pm.CarAppBlockingPolicy.equals
-android.car.content.pm.CarAppBlockingPolicy.toString
-android.car.content.pm.CarAppBlockingPolicyService.SERVICE_INTERFACE
-android.car.content.pm.CarAppBlockingPolicyService.getAppBlockingPolicy
-android.car.content.pm.CarAppBlockingPolicyService.onStartCommand
-android.car.content.pm.CarAppBlockingPolicyService.onBind
-android.car.content.pm.CarAppBlockingPolicyService.onUnbind
-android.car.content.pm.CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE
-android.car.content.pm.CarPackageManager.FLAG_SET_POLICY_ADD
-android.car.content.pm.CarPackageManager.FLAG_SET_POLICY_REMOVE
-android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME
-android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID
-android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME
-android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO
-android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_DISPLAY_ID
-android.car.content.pm.CarPackageManager.DRIVING_SAFETY_REGION_ALL
-android.car.content.pm.CarPackageManager.DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS
-android.car.content.pm.CarPackageManager.ERROR_CODE_NO_PACKAGE
-android.car.content.pm.CarPackageManager.onCarDisconnected
-android.car.content.pm.CarPackageManager.setAppBlockingPolicy
-android.car.content.pm.CarPackageManager.restartTask
-android.car.content.pm.CarPackageManager.isActivityBackedBySafeActivity
-android.car.content.pm.CarPackageManager.setEnableActivityBlocking
-android.car.content.pm.CarPackageManager.isActivityDistractionOptimized
-android.car.content.pm.CarPackageManager.isPendingIntentDistractionOptimized
-android.car.content.pm.CarPackageManager.isServiceDistractionOptimized
-android.car.content.pm.CarPackageManager.getCurrentDrivingSafetyRegion
-android.car.content.pm.CarPackageManager.controlTemporaryActivityBlockingBypassingAsUser
-android.car.content.pm.CarPackageManager.getSupportedDrivingSafetyRegionsForActivityAsUser
-android.car.content.pm.CarPackageManager.getTargetCarVersion
-android.car.content.pm.CarPackageManager.getTargetCarVersion
-android.car.diagnostic.CarDiagnosticEvent.frameType
-android.car.diagnostic.CarDiagnosticEvent.timestamp
-android.car.diagnostic.CarDiagnosticEvent.dtc
-android.car.diagnostic.CarDiagnosticEvent.CREATOR
-android.car.diagnostic.CarDiagnosticEvent.describeContents
-android.car.diagnostic.CarDiagnosticEvent.writeToParcel
-android.car.diagnostic.CarDiagnosticEvent.writeToJson
-android.car.diagnostic.CarDiagnosticEvent.withVendorSensorsRemoved
-android.car.diagnostic.CarDiagnosticEvent.isLiveFrame
-android.car.diagnostic.CarDiagnosticEvent.isFreezeFrame
-android.car.diagnostic.CarDiagnosticEvent.isEmptyFrame
-android.car.diagnostic.CarDiagnosticEvent.checkLiveFrame
-android.car.diagnostic.CarDiagnosticEvent.checkFreezeFrame
-android.car.diagnostic.CarDiagnosticEvent.isEarlierThan
-android.car.diagnostic.CarDiagnosticEvent.equals
-android.car.diagnostic.CarDiagnosticEvent.hashCode
-android.car.diagnostic.CarDiagnosticEvent.toString
-android.car.diagnostic.CarDiagnosticEvent.getSystemIntegerSensor
-android.car.diagnostic.CarDiagnosticEvent.getSystemFloatSensor
-android.car.diagnostic.CarDiagnosticEvent.getVendorIntegerSensor
-android.car.diagnostic.CarDiagnosticEvent.getVendorFloatSensor
-android.car.diagnostic.CarDiagnosticEvent.getSystemIntegerSensor
-android.car.diagnostic.CarDiagnosticEvent.getSystemFloatSensor
-android.car.diagnostic.CarDiagnosticEvent.getVendorIntegerSensor
-android.car.diagnostic.CarDiagnosticEvent.getVendorFloatSensor
-android.car.diagnostic.CarDiagnosticEvent.getFuelSystemStatus
-android.car.diagnostic.CarDiagnosticEvent.getSecondaryAirStatus
-android.car.diagnostic.CarDiagnosticEvent.getIgnitionMonitors
-android.car.diagnostic.CarDiagnosticEvent.getFuelType
-android.car.diagnostic.CarDiagnosticEvent.Builder.newLiveFrameBuilder
-android.car.diagnostic.CarDiagnosticEvent.Builder.newFreezeFrameBuilder
-android.car.diagnostic.CarDiagnosticEvent.Builder.atTimestamp
-android.car.diagnostic.CarDiagnosticEvent.Builder.setTimeStamp
-android.car.diagnostic.CarDiagnosticEvent.Builder.withIntValue
-android.car.diagnostic.CarDiagnosticEvent.Builder.setIntValue
-android.car.diagnostic.CarDiagnosticEvent.Builder.withFloatValue
-android.car.diagnostic.CarDiagnosticEvent.Builder.setFloatValue
-android.car.diagnostic.CarDiagnosticEvent.Builder.withDtc
-android.car.diagnostic.CarDiagnosticEvent.Builder.setDtc
-android.car.diagnostic.CarDiagnosticEvent.Builder.build
-android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.OPEN_INSUFFICIENT_ENGINE_TEMPERATURE
-android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.CLOSED_LOOP
-android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION
-android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.OPEN_SYSTEM_FAILURE
-android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.CLOSED_LOOP_BUT_FEEDBACK_FAULT
-android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.UPSTREAM
-android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.DOWNSTREAM_OF_CATALYCIC_CONVERTER
-android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.FROM_OUTSIDE_OR_OFF
-android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.PUMP_ON_FOR_DIAGNOSTICS
-android.car.diagnostic.CarDiagnosticEvent.FuelType.NOT_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.GASOLINE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.METHANOL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.ETHANOL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.DIESEL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.LPG
-android.car.diagnostic.CarDiagnosticEvent.FuelType.CNG
-android.car.diagnostic.CarDiagnosticEvent.FuelType.PROPANE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.ELECTRIC
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_GASOLINE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_METHANOL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_ETHANOL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_LPG
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_CNG
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_PROPANE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_ELECTRIC
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_GASOLINE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_ETHANOL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_DIESEL
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_ELECTRIC
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION
-android.car.diagnostic.CarDiagnosticEvent.FuelType.HYBRID_REGENERATIVE
-android.car.diagnostic.CarDiagnosticEvent.FuelType.BIFUEL_RUNNING_DIESEL
-android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor.available
-android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor.incomplete
-android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor.Decoder.fromValue
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.components
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.fuelSystem
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.misfire
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.COMPONENTS_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.COMPONENTS_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.FUEL_SYSTEM_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.FUEL_SYSTEM_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.MISFIRE_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.MISFIRE_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.asSparkIgnitionMonitors
-android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.asCompressionIgnitionMonitors
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.EGR
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.oxygenSensorHeater
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.oxygenSensor
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.ACRefrigerant
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.secondaryAirSystem
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.evaporativeSystem
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.heatedCatalyst
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.catalyst
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.EGR_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.EGR_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.OXYGEN_SENSOR_HEATER_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.OXYGEN_SENSOR_HEATER_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.OXYGEN_SENSOR_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.OXYGEN_SENSOR_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.AC_REFRIGERANT_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.AC_REFRIGERANT_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.SECONDARY_AIR_SYSTEM_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.SECONDARY_AIR_SYSTEM_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.EVAPORATIVE_SYSTEM_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.EVAPORATIVE_SYSTEM_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.HEATED_CATALYST_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.HEATED_CATALYST_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.CATALYST_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.CATALYST_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.EGROrVVT
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.PMFilter
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.exhaustGasSensor
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.boostPressure
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NOxSCR
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NMHCCatalyst
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.EGR_OR_VVT_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.EGR_OR_VVT_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.PM_FILTER_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.PM_FILTER_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.EXHAUST_GAS_SENSOR_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.EXHAUST_GAS_SENSOR_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.BOOST_PRESSURE_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.BOOST_PRESSURE_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NOx_SCR_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NOx_SCR_INCOMPLETE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NMHC_CATALYST_AVAILABLE
-android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.NMHC_CATALYST_INCOMPLETE
-android.car.diagnostic.CarDiagnosticManager.FRAME_TYPE_LIVE
-android.car.diagnostic.CarDiagnosticManager.FRAME_TYPE_FREEZE
-android.car.diagnostic.CarDiagnosticManager.FRAME_TYPES
-android.car.diagnostic.CarDiagnosticManager.onCarDisconnected
-android.car.diagnostic.CarDiagnosticManager.registerListener
-android.car.diagnostic.CarDiagnosticManager.unregisterListener
-android.car.diagnostic.CarDiagnosticManager.getLatestLiveFrame
-android.car.diagnostic.CarDiagnosticManager.getFreezeFrameTimestamps
-android.car.diagnostic.CarDiagnosticManager.getFreezeFrame
-android.car.diagnostic.CarDiagnosticManager.clearFreezeFrames
-android.car.diagnostic.CarDiagnosticManager.isLiveFrameSupported
-android.car.diagnostic.CarDiagnosticManager.isFreezeFrameNotificationSupported
-android.car.diagnostic.CarDiagnosticManager.isGetFreezeFrameSupported
-android.car.diagnostic.CarDiagnosticManager.isClearFreezeFramesSupported
-android.car.diagnostic.CarDiagnosticManager.isSelectiveClearFreezeFramesSupported
-android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener.onDiagnosticEvent
-android.car.diagnostic.FloatSensorIndex.CALCULATED_ENGINE_LOAD
-android.car.diagnostic.FloatSensorIndex.ENGINE_COOLANT_TEMPERATURE
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK1
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK1
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK2
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK2
-android.car.diagnostic.FloatSensorIndex.FUEL_PRESSURE
-android.car.diagnostic.FloatSensorIndex.INTAKE_MANIFOLD_ABSOLUTE_PRESSURE
-android.car.diagnostic.FloatSensorIndex.ENGINE_RPM
-android.car.diagnostic.FloatSensorIndex.VEHICLE_SPEED
-android.car.diagnostic.FloatSensorIndex.TIMING_ADVANCE
-android.car.diagnostic.FloatSensorIndex.MAF_AIR_FLOW_RATE
-android.car.diagnostic.FloatSensorIndex.THROTTLE_POSITION
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR2_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR3_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR4_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR7_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR8_VOLTAGE
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM
-android.car.diagnostic.FloatSensorIndex.OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.FUEL_RAIL_PRESSURE
-android.car.diagnostic.FloatSensorIndex.FUEL_RAIL_GAUGE_PRESSURE
-android.car.diagnostic.FloatSensorIndex.COMMANDED_EXHAUST_GAS_RECIRCULATION
-android.car.diagnostic.FloatSensorIndex.EXHAUST_GAS_RECIRCULATION_ERROR
-android.car.diagnostic.FloatSensorIndex.COMMANDED_EVAPORATIVE_PURGE
-android.car.diagnostic.FloatSensorIndex.FUEL_TANK_LEVEL_INPUT
-android.car.diagnostic.FloatSensorIndex.EVAPORATION_SYSTEM_VAPOR_PRESSURE
-android.car.diagnostic.FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR1
-android.car.diagnostic.FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR1
-android.car.diagnostic.FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR2
-android.car.diagnostic.FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR2
-android.car.diagnostic.FloatSensorIndex.ABSOLUTE_LOAD_VALUE
-android.car.diagnostic.FloatSensorIndex.FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO
-android.car.diagnostic.FloatSensorIndex.RELATIVE_THROTTLE_POSITION
-android.car.diagnostic.FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_B
-android.car.diagnostic.FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_C
-android.car.diagnostic.FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_D
-android.car.diagnostic.FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_E
-android.car.diagnostic.FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_F
-android.car.diagnostic.FloatSensorIndex.COMMANDED_THROTTLE_ACTUATOR
-android.car.diagnostic.FloatSensorIndex.ETHANOL_FUEL_PERCENTAGE
-android.car.diagnostic.FloatSensorIndex.ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3
-android.car.diagnostic.FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3
-android.car.diagnostic.FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4
-android.car.diagnostic.FloatSensorIndex.RELATIVE_ACCELERATOR_PEDAL_POSITION
-android.car.diagnostic.FloatSensorIndex.HYBRID_BATTERY_PACK_REMAINING_LIFE
-android.car.diagnostic.FloatSensorIndex.FUEL_INJECTION_TIMING
-android.car.diagnostic.FloatSensorIndex.ENGINE_FUEL_RATE
-android.car.diagnostic.FloatSensorIndex.LAST_SYSTEM
-android.car.diagnostic.FloatSensorIndex.VENDOR_START
-android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS
-android.car.diagnostic.IntegerSensorIndex.MALFUNCTION_INDICATOR_LIGHT_ON
-android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED
-android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS
-android.car.diagnostic.IntegerSensorIndex.INTAKE_AIR_TEMPERATURE
-android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS
-android.car.diagnostic.IntegerSensorIndex.NUM_OXYGEN_SENSORS_PRESENT
-android.car.diagnostic.IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START
-android.car.diagnostic.IntegerSensorIndex.DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON
-android.car.diagnostic.IntegerSensorIndex.WARMUPS_SINCE_CODES_CLEARED
-android.car.diagnostic.IntegerSensorIndex.DISTANCE_TRAVELED_SINCE_CODES_CLEARED
-android.car.diagnostic.IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE
-android.car.diagnostic.IntegerSensorIndex.CONTROL_MODULE_VOLTAGE
-android.car.diagnostic.IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE
-android.car.diagnostic.IntegerSensorIndex.TIME_WITH_MALFUNCTION_LIGHT_ON
-android.car.diagnostic.IntegerSensorIndex.TIME_SINCE_TROUBLE_CODES_CLEARED
-android.car.diagnostic.IntegerSensorIndex.MAX_FUEL_AIR_EQUIVALENCE_RATIO
-android.car.diagnostic.IntegerSensorIndex.MAX_OXYGEN_SENSOR_VOLTAGE
-android.car.diagnostic.IntegerSensorIndex.MAX_OXYGEN_SENSOR_CURRENT
-android.car.diagnostic.IntegerSensorIndex.MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE
-android.car.diagnostic.IntegerSensorIndex.MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR
-android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE
-android.car.diagnostic.IntegerSensorIndex.FUEL_RAIL_ABSOLUTE_PRESSURE
-android.car.diagnostic.IntegerSensorIndex.ENGINE_OIL_TEMPERATURE
-android.car.diagnostic.IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE
-android.car.diagnostic.IntegerSensorIndex.ENGINE_ACTUAL_PERCENT_TORQUE
-android.car.diagnostic.IntegerSensorIndex.ENGINE_REFERENCE_PERCENT_TORQUE
-android.car.diagnostic.IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_IDLE
-android.car.diagnostic.IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT1
-android.car.diagnostic.IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT2
-android.car.diagnostic.IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT3
-android.car.diagnostic.IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT4
-android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM
-android.car.diagnostic.IntegerSensorIndex.VENDOR_START
-android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_UNKNOWN
-android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED
-android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING
-android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING
-android.car.drivingstate.CarDrivingStateEvent.timeStamp
-android.car.drivingstate.CarDrivingStateEvent.eventValue
-android.car.drivingstate.CarDrivingStateEvent.CREATOR
-android.car.drivingstate.CarDrivingStateEvent.describeContents
-android.car.drivingstate.CarDrivingStateEvent.writeToParcel
-android.car.drivingstate.CarDrivingStateEvent.toString
-android.car.drivingstate.CarDrivingStateManager.onCarDisconnected
-android.car.drivingstate.CarDrivingStateManager.registerListener
-android.car.drivingstate.CarDrivingStateManager.unregisterListener
-android.car.drivingstate.CarDrivingStateManager.getCurrentCarDrivingState
-android.car.drivingstate.CarDrivingStateManager.injectDrivingState
-android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener.onDrivingStateChanged
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_BASELINE
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION
-android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED
-android.car.drivingstate.CarUxRestrictions.CREATOR
-android.car.drivingstate.CarUxRestrictions.getTimeStamp
-android.car.drivingstate.CarUxRestrictions.isRequiresDistractionOptimization
-android.car.drivingstate.CarUxRestrictions.getActiveRestrictions
-android.car.drivingstate.CarUxRestrictions.getMaxRestrictedStringLength
-android.car.drivingstate.CarUxRestrictions.getMaxCumulativeContentItems
-android.car.drivingstate.CarUxRestrictions.getMaxContentDepth
-android.car.drivingstate.CarUxRestrictions.describeContents
-android.car.drivingstate.CarUxRestrictions.writeToParcel
-android.car.drivingstate.CarUxRestrictions.toString
-android.car.drivingstate.CarUxRestrictions.isSameRestrictions
-android.car.drivingstate.CarUxRestrictions.Builder.setMaxStringLength
-android.car.drivingstate.CarUxRestrictions.Builder.setMaxCumulativeContentItems
-android.car.drivingstate.CarUxRestrictions.Builder.setMaxContentDepth
-android.car.drivingstate.CarUxRestrictions.Builder.build
-android.car.drivingstate.CarUxRestrictionsConfiguration.CREATOR
-android.car.drivingstate.CarUxRestrictionsConfiguration.getUxRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.getUxRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.getPhysicalPort
-android.car.drivingstate.CarUxRestrictionsConfiguration.getOccupantZoneId
-android.car.drivingstate.CarUxRestrictionsConfiguration.getDisplayType
-android.car.drivingstate.CarUxRestrictionsConfiguration.writeJson
-android.car.drivingstate.CarUxRestrictionsConfiguration.toString
-android.car.drivingstate.CarUxRestrictionsConfiguration.readJson
-android.car.drivingstate.CarUxRestrictionsConfiguration.hashCode
-android.car.drivingstate.CarUxRestrictionsConfiguration.equals
-android.car.drivingstate.CarUxRestrictionsConfiguration.hasSameParameters
-android.car.drivingstate.CarUxRestrictionsConfiguration.dump
-android.car.drivingstate.CarUxRestrictionsConfiguration.describeContents
-android.car.drivingstate.CarUxRestrictionsConfiguration.writeToParcel
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.mRestrictionModes
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.validatePort
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.validateOccupantZoneId
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.validateDisplayType
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setPhysicalPort
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setOccupantZoneId
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setDisplayType
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setUxRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setUxRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setUxRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setMaxStringLength
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setMaxCumulativeContentItems
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.setMaxContentDepth
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.build
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.MAX_SPEED
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.includes
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.compareTo
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.hashCode
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.equals
-android.car.drivingstate.CarUxRestrictionsConfiguration.Builder.SpeedRange.toString
-android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions.setDistractionOptimizationRequired
-android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions.setRestrictions
-android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions.setMode
-android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions.setSpeedRange
-android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions.toString
-android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE
-android.car.drivingstate.CarUxRestrictionsManager.onCarDisconnected
-android.car.drivingstate.CarUxRestrictionsManager.registerListener
-android.car.drivingstate.CarUxRestrictionsManager.registerListener
-android.car.drivingstate.CarUxRestrictionsManager.setListener
-android.car.drivingstate.CarUxRestrictionsManager.unregisterListener
-android.car.drivingstate.CarUxRestrictionsManager.saveUxRestrictionsConfigurationForNextBoot
-android.car.drivingstate.CarUxRestrictionsManager.getCurrentCarUxRestrictions
-android.car.drivingstate.CarUxRestrictionsManager.getCurrentCarUxRestrictions
-android.car.drivingstate.CarUxRestrictionsManager.setRestrictionMode
-android.car.drivingstate.CarUxRestrictionsManager.getRestrictionMode
-android.car.drivingstate.CarUxRestrictionsManager.saveUxRestrictionsConfigurationForNextBoot
-android.car.drivingstate.CarUxRestrictionsManager.getStagedConfigs
-android.car.drivingstate.CarUxRestrictionsManager.getConfigs
-android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener.onUxRestrictionsChanged
-android.car.evs.CarEvsBufferDescriptor.CREATOR
-android.car.evs.CarEvsBufferDescriptor.describeContents
-android.car.evs.CarEvsBufferDescriptor.writeToParcel
-android.car.evs.CarEvsBufferDescriptor.toString
-android.car.evs.CarEvsBufferDescriptor.finalize
-android.car.evs.CarEvsBufferDescriptor.close
-android.car.evs.CarEvsBufferDescriptor.getId
-android.car.evs.CarEvsBufferDescriptor.getHardwareBuffer
-android.car.evs.CarEvsManager.EXTRA_SESSION_TOKEN
-android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW
-android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW
-android.car.evs.CarEvsManager.SERVICE_STATE_UNAVAILABLE
-android.car.evs.CarEvsManager.SERVICE_STATE_INACTIVE
-android.car.evs.CarEvsManager.SERVICE_STATE_REQUESTED
-android.car.evs.CarEvsManager.SERVICE_STATE_ACTIVE
-android.car.evs.CarEvsManager.STREAM_EVENT_NONE
-android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STARTED
-android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED
-android.car.evs.CarEvsManager.STREAM_EVENT_FRAME_DROPPED
-android.car.evs.CarEvsManager.STREAM_EVENT_TIMEOUT
-android.car.evs.CarEvsManager.STREAM_EVENT_PARAMETER_CHANGED
-android.car.evs.CarEvsManager.STREAM_EVENT_PRIMARY_OWNER_CHANGED
-android.car.evs.CarEvsManager.STREAM_EVENT_OTHER_ERRORS
-android.car.evs.CarEvsManager.ERROR_NONE
-android.car.evs.CarEvsManager.ERROR_UNAVAILABLE
-android.car.evs.CarEvsManager.ERROR_BUSY
-android.car.evs.CarEvsManager.onCarDisconnected
-android.car.evs.CarEvsManager.setStatusListener
-android.car.evs.CarEvsManager.clearStatusListener
-android.car.evs.CarEvsManager.returnFrameBuffer
-android.car.evs.CarEvsManager.startActivity
-android.car.evs.CarEvsManager.stopActivity
-android.car.evs.CarEvsManager.startVideoStream
-android.car.evs.CarEvsManager.stopVideoStream
-android.car.evs.CarEvsManager.getCurrentStatus
-android.car.evs.CarEvsManager.generateSessionToken
-android.car.evs.CarEvsManager.isSupported
-android.car.evs.CarEvsManager.CarEvsStatusListener.onStatusChanged
-android.car.evs.CarEvsManager.CarEvsStreamCallback.onStreamEvent
-android.car.evs.CarEvsManager.CarEvsStreamCallback.onNewFrame
-android.car.evs.CarEvsStatus.CREATOR
-android.car.evs.CarEvsStatus.describeContents
-android.car.evs.CarEvsStatus.writeToParcel
-android.car.evs.CarEvsStatus.toString
-android.car.evs.CarEvsStatus.getState
-android.car.evs.CarEvsStatus.getServiceType
-android.car.hardware.CarHvacFanDirection.UNKNOWN
-android.car.hardware.CarHvacFanDirection.FACE
-android.car.hardware.CarHvacFanDirection.FLOOR
-android.car.hardware.CarHvacFanDirection.FACE_AND_FLOOR
-android.car.hardware.CarHvacFanDirection.DEFROST
-android.car.hardware.CarHvacFanDirection.DEFROST_AND_FLOOR
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE
-android.car.hardware.CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS
-android.car.hardware.CarPropertyConfig.CREATOR
-android.car.hardware.CarPropertyConfig.getAccess
-android.car.hardware.CarPropertyConfig.getAreaType
-android.car.hardware.CarPropertyConfig.getChangeMode
-android.car.hardware.CarPropertyConfig.getConfigArray
-android.car.hardware.CarPropertyConfig.getConfigString
-android.car.hardware.CarPropertyConfig.getMaxSampleRate
-android.car.hardware.CarPropertyConfig.getMinSampleRate
-android.car.hardware.CarPropertyConfig.getPropertyId
-android.car.hardware.CarPropertyConfig.getPropertyType
-android.car.hardware.CarPropertyConfig.getAreaIdConfigs
-android.car.hardware.CarPropertyConfig.getAreaIdConfig
-android.car.hardware.CarPropertyConfig.isGlobalProperty
-android.car.hardware.CarPropertyConfig.getAreaCount
-android.car.hardware.CarPropertyConfig.getAreaIds
-android.car.hardware.CarPropertyConfig.getFirstAndOnlyAreaId
-android.car.hardware.CarPropertyConfig.hasArea
-android.car.hardware.CarPropertyConfig.getMinValue
-android.car.hardware.CarPropertyConfig.getMaxValue
-android.car.hardware.CarPropertyConfig.getMinValue
-android.car.hardware.CarPropertyConfig.getMaxValue
-android.car.hardware.CarPropertyConfig.describeContents
-android.car.hardware.CarPropertyConfig.writeToParcel
-android.car.hardware.CarPropertyConfig.toString
-android.car.hardware.CarPropertyConfig.newBuilder
-android.car.hardware.CarPropertyConfig.newBuilder
-android.car.hardware.CarPropertyConfig.AreaConfig.CREATOR
-android.car.hardware.CarPropertyConfig.AreaConfig.getMinValue
-android.car.hardware.CarPropertyConfig.AreaConfig.getMaxValue
-android.car.hardware.CarPropertyConfig.AreaConfig.describeContents
-android.car.hardware.CarPropertyConfig.AreaConfig.writeToParcel
-android.car.hardware.CarPropertyConfig.AreaConfig.toString
-android.car.hardware.CarPropertyConfig.Builder.addAreas
-android.car.hardware.CarPropertyConfig.Builder.addArea
-android.car.hardware.CarPropertyConfig.Builder.addAreaConfig
-android.car.hardware.CarPropertyConfig.Builder.addAreaIdConfig
-android.car.hardware.CarPropertyConfig.Builder.setAccess
-android.car.hardware.CarPropertyConfig.Builder.setChangeMode
-android.car.hardware.CarPropertyConfig.Builder.setConfigArray
-android.car.hardware.CarPropertyConfig.Builder.setConfigString
-android.car.hardware.CarPropertyConfig.Builder.setMaxSampleRate
-android.car.hardware.CarPropertyConfig.Builder.setMinSampleRate
-android.car.hardware.CarPropertyConfig.Builder.build
-android.car.hardware.CarPropertyValue.STATUS_AVAILABLE
-android.car.hardware.CarPropertyValue.STATUS_UNAVAILABLE
-android.car.hardware.CarPropertyValue.STATUS_ERROR
-android.car.hardware.CarPropertyValue.CREATOR
-android.car.hardware.CarPropertyValue.describeContents
-android.car.hardware.CarPropertyValue.writeToParcel
-android.car.hardware.CarPropertyValue.getPropertyId
-android.car.hardware.CarPropertyValue.getAreaId
-android.car.hardware.CarPropertyValue.getStatus
-android.car.hardware.CarPropertyValue.getTimestamp
-android.car.hardware.CarPropertyValue.getValue
-android.car.hardware.CarPropertyValue.toString
-android.car.hardware.CarPropertyValue.hashCode
-android.car.hardware.CarPropertyValue.equals
-android.car.hardware.CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS
-android.car.hardware.CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK
-android.car.hardware.CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK
-android.car.hardware.CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK
-android.car.hardware.CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK
-android.car.hardware.CarSensorConfig.CREATOR
-android.car.hardware.CarSensorConfig.describeContents
-android.car.hardware.CarSensorConfig.writeToParcel
-android.car.hardware.CarSensorConfig.getBundle
-android.car.hardware.CarSensorConfig.getInt
-android.car.hardware.CarSensorConfig.getType
-android.car.hardware.CarSensorConfig.toString
-android.car.hardware.CarSensorEvent.GEAR_NEUTRAL
-android.car.hardware.CarSensorEvent.GEAR_FIRST
-android.car.hardware.CarSensorEvent.GEAR_SECOND
-android.car.hardware.CarSensorEvent.GEAR_THIRD
-android.car.hardware.CarSensorEvent.GEAR_FOURTH
-android.car.hardware.CarSensorEvent.GEAR_FIFTH
-android.car.hardware.CarSensorEvent.GEAR_SIXTH
-android.car.hardware.CarSensorEvent.GEAR_SEVENTH
-android.car.hardware.CarSensorEvent.GEAR_EIGHTH
-android.car.hardware.CarSensorEvent.GEAR_NINTH
-android.car.hardware.CarSensorEvent.GEAR_TENTH
-android.car.hardware.CarSensorEvent.GEAR_DRIVE
-android.car.hardware.CarSensorEvent.GEAR_PARK
-android.car.hardware.CarSensorEvent.GEAR_REVERSE
-android.car.hardware.CarSensorEvent.IGNITION_STATE_UNDEFINED
-android.car.hardware.CarSensorEvent.IGNITION_STATE_LOCK
-android.car.hardware.CarSensorEvent.IGNITION_STATE_OFF
-android.car.hardware.CarSensorEvent.IGNITION_STATE_ACC
-android.car.hardware.CarSensorEvent.IGNITION_STATE_ON
-android.car.hardware.CarSensorEvent.IGNITION_STATE_START
-android.car.hardware.CarSensorEvent.INDEX_ENVIRONMENT_TEMPERATURE
-android.car.hardware.CarSensorEvent.INDEX_WHEEL_DISTANCE_RESET_COUNT
-android.car.hardware.CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT
-android.car.hardware.CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_RIGHT
-android.car.hardware.CarSensorEvent.INDEX_WHEEL_DISTANCE_REAR_RIGHT
-android.car.hardware.CarSensorEvent.INDEX_WHEEL_DISTANCE_REAR_LEFT
-android.car.hardware.CarSensorEvent.sensorType
-android.car.hardware.CarSensorEvent.timestamp
-android.car.hardware.CarSensorEvent.floatValues
-android.car.hardware.CarSensorEvent.intValues
-android.car.hardware.CarSensorEvent.longValues
-android.car.hardware.CarSensorEvent.CREATOR
-android.car.hardware.CarSensorEvent.describeContents
-android.car.hardware.CarSensorEvent.writeToParcel
-android.car.hardware.CarSensorEvent.getEnvironmentData
-android.car.hardware.CarSensorEvent.getIgnitionStateData
-android.car.hardware.CarSensorEvent.getNightData
-android.car.hardware.CarSensorEvent.getGearData
-android.car.hardware.CarSensorEvent.getParkingBrakeData
-android.car.hardware.CarSensorEvent.getFuelLevelData
-android.car.hardware.CarSensorEvent.getOdometerData
-android.car.hardware.CarSensorEvent.getRpmData
-android.car.hardware.CarSensorEvent.getCarSpeedData
-android.car.hardware.CarSensorEvent.getCarWheelTickDistanceData
-android.car.hardware.CarSensorEvent.getCarAbsActiveData
-android.car.hardware.CarSensorEvent.getCarTractionControlActiveData
-android.car.hardware.CarSensorEvent.getCarFuelDoorOpenData
-android.car.hardware.CarSensorEvent.getCarEvBatteryLevelData
-android.car.hardware.CarSensorEvent.getCarEvChargePortOpenData
-android.car.hardware.CarSensorEvent.getCarEvChargePortConnectedData
-android.car.hardware.CarSensorEvent.getCarEvBatteryChargeRateData
-android.car.hardware.CarSensorEvent.getCarEngineOilLevelData
-android.car.hardware.CarSensorEvent.toString
-android.car.hardware.CarSensorEvent.EnvironmentData.timestamp
-android.car.hardware.CarSensorEvent.EnvironmentData.temperature
-android.car.hardware.CarSensorEvent.IgnitionStateData.timestamp
-android.car.hardware.CarSensorEvent.IgnitionStateData.ignitionState
-android.car.hardware.CarSensorEvent.NightData.timestamp
-android.car.hardware.CarSensorEvent.NightData.isNightMode
-android.car.hardware.CarSensorEvent.GearData.timestamp
-android.car.hardware.CarSensorEvent.GearData.gear
-android.car.hardware.CarSensorEvent.ParkingBrakeData.timestamp
-android.car.hardware.CarSensorEvent.ParkingBrakeData.isEngaged
-android.car.hardware.CarSensorEvent.FuelLevelData.timestamp
-android.car.hardware.CarSensorEvent.FuelLevelData.level
-android.car.hardware.CarSensorEvent.OdometerData.timestamp
-android.car.hardware.CarSensorEvent.OdometerData.kms
-android.car.hardware.CarSensorEvent.RpmData.timestamp
-android.car.hardware.CarSensorEvent.RpmData.rpm
-android.car.hardware.CarSensorEvent.CarSpeedData.timestamp
-android.car.hardware.CarSensorEvent.CarSpeedData.carSpeed
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.timestamp
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.sensorResetCount
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.frontLeftWheelDistanceMm
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.frontRightWheelDistanceMm
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.rearRightWheelDistanceMm
-android.car.hardware.CarSensorEvent.CarWheelTickDistanceData.rearLeftWheelDistanceMm
-android.car.hardware.CarSensorEvent.CarAbsActiveData.timestamp
-android.car.hardware.CarSensorEvent.CarAbsActiveData.absIsActive
-android.car.hardware.CarSensorEvent.CarTractionControlActiveData.timestamp
-android.car.hardware.CarSensorEvent.CarTractionControlActiveData.tractionControlIsActive
-android.car.hardware.CarSensorEvent.CarFuelDoorOpenData.timestamp
-android.car.hardware.CarSensorEvent.CarFuelDoorOpenData.fuelDoorIsOpen
-android.car.hardware.CarSensorEvent.CarEvBatteryLevelData.timestamp
-android.car.hardware.CarSensorEvent.CarEvBatteryLevelData.evBatteryLevel
-android.car.hardware.CarSensorEvent.CarEvChargePortOpenData.timestamp
-android.car.hardware.CarSensorEvent.CarEvChargePortOpenData.evChargePortIsOpen
-android.car.hardware.CarSensorEvent.CarEvChargePortConnectedData.timestamp
-android.car.hardware.CarSensorEvent.CarEvChargePortConnectedData.evChargePortIsConnected
-android.car.hardware.CarSensorEvent.CarEvBatteryChargeRateData.timestamp
-android.car.hardware.CarSensorEvent.CarEvBatteryChargeRateData.evChargeRate
-android.car.hardware.CarSensorEvent.CarEngineOilLevelData.timestamp
-android.car.hardware.CarSensorEvent.CarEngineOilLevelData.engineOilLevel
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED1
-android.car.hardware.CarSensorManager.SENSOR_TYPE_CAR_SPEED
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RPM
-android.car.hardware.CarSensorManager.SENSOR_TYPE_ODOMETER
-android.car.hardware.CarSensorManager.SENSOR_TYPE_FUEL_LEVEL
-android.car.hardware.CarSensorManager.SENSOR_TYPE_PARKING_BRAKE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_GEAR
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED8
-android.car.hardware.CarSensorManager.SENSOR_TYPE_NIGHT
-android.car.hardware.CarSensorManager.SENSOR_TYPE_ENV_OUTSIDE_TEMPERATURE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED10
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED11
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED12
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED13
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED14
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED15
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED16
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED17
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED18
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED19
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED20
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED21
-android.car.hardware.CarSensorManager.SENSOR_TYPE_IGNITION_STATE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_ABS_ACTIVE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_RESERVED26
-android.car.hardware.CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN
-android.car.hardware.CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL
-android.car.hardware.CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_OPEN
-android.car.hardware.CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED
-android.car.hardware.CarSensorManager.SENSOR_TYPE_EV_BATTERY_CHARGE_RATE
-android.car.hardware.CarSensorManager.SENSOR_TYPE_ENGINE_OIL_LEVEL
-android.car.hardware.CarSensorManager.SENSOR_RATE_ONCHANGE
-android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL
-android.car.hardware.CarSensorManager.SENSOR_RATE_UI
-android.car.hardware.CarSensorManager.SENSOR_RATE_FAST
-android.car.hardware.CarSensorManager.SENSOR_RATE_FASTEST
-android.car.hardware.CarSensorManager.onCarDisconnected
-android.car.hardware.CarSensorManager.getSupportedSensors
-android.car.hardware.CarSensorManager.getPropertyList
-android.car.hardware.CarSensorManager.isSensorSupported
-android.car.hardware.CarSensorManager.isSensorSupported
-android.car.hardware.CarSensorManager.registerListener
-android.car.hardware.CarSensorManager.unregisterListener
-android.car.hardware.CarSensorManager.unregisterListener
-android.car.hardware.CarSensorManager.getLatestSensorEvent
-android.car.hardware.CarSensorManager.getSensorConfig
-android.car.hardware.CarSensorManager.OnSensorChangedListener.onSensorChanged
-android.car.hardware.CarVendorExtensionManager.registerCallback
-android.car.hardware.CarVendorExtensionManager.unregisterCallback
-android.car.hardware.CarVendorExtensionManager.getProperties
-android.car.hardware.CarVendorExtensionManager.isPropertyAvailable
-android.car.hardware.CarVendorExtensionManager.getGlobalProperty
-android.car.hardware.CarVendorExtensionManager.getProperty
-android.car.hardware.CarVendorExtensionManager.setGlobalProperty
-android.car.hardware.CarVendorExtensionManager.setProperty
-android.car.hardware.CarVendorExtensionManager.onCarDisconnected
-android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback.onChangeEvent
-android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback.onErrorEvent
-android.car.hardware.cabin.CarCabinManager.ID_DOOR_POS
-android.car.hardware.cabin.CarCabinManager.ID_DOOR_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_DOOR_LOCK
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_Z_POS
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_Z_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_Y_POS
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_Y_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_LOCK
-android.car.hardware.cabin.CarCabinManager.ID_MIRROR_FOLD
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_MEMORY_SELECT
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_MEMORY_SET
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BELT_BUCKLED
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BELT_HEIGHT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BELT_HEIGHT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_FORE_AFT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_FORE_AFT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BACKREST_ANGLE_1_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BACKREST_ANGLE_1_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BACKREST_ANGLE_2_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_BACKREST_ANGLE_2_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEIGHT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEIGHT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_DEPTH_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_DEPTH_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_TILT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_TILT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_LUMBAR_FORE_AFT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_LUMBAR_FORE_AFT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_LUMBAR_SIDE_SUPPORT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_HEIGHT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_HEIGHT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_ANGLE_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_ANGLE_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_FORE_AFT_POS
-android.car.hardware.cabin.CarCabinManager.ID_SEAT_HEADREST_FORE_AFT_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_WINDOW_POS
-android.car.hardware.cabin.CarCabinManager.ID_WINDOW_MOVE
-android.car.hardware.cabin.CarCabinManager.ID_WINDOW_LOCK
-android.car.hardware.cabin.CarCabinManager.isZonedProperty
-android.car.hardware.cabin.CarCabinManager.registerCallback
-android.car.hardware.cabin.CarCabinManager.unregisterCallback
-android.car.hardware.cabin.CarCabinManager.getPropertyList
-android.car.hardware.cabin.CarCabinManager.getBooleanProperty
-android.car.hardware.cabin.CarCabinManager.getFloatProperty
-android.car.hardware.cabin.CarCabinManager.getIntProperty
-android.car.hardware.cabin.CarCabinManager.setBooleanProperty
-android.car.hardware.cabin.CarCabinManager.setFloatProperty
-android.car.hardware.cabin.CarCabinManager.setIntProperty
-android.car.hardware.cabin.CarCabinManager.onCarDisconnected
-android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback.onChangeEvent
-android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback.onErrorEvent
-android.car.hardware.hvac.CarHvacManager.ID_MIRROR_DEFROSTER_ON
-android.car.hardware.hvac.CarHvacManager.ID_STEERING_WHEEL_HEAT
-android.car.hardware.hvac.CarHvacManager.ID_OUTSIDE_AIR_TEMP
-android.car.hardware.hvac.CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_TEMP_SETPOINT
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_TEMP_ACTUAL
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_HVAC_POWER_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_FAN_SPEED_RPM
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_FAN_DIRECTION_AVAILABLE
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_FAN_DIRECTION
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_SEAT_TEMP
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_AC_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_MAX_AC_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_DUAL_ZONE_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_MAX_DEFROST_ON
-android.car.hardware.hvac.CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON
-android.car.hardware.hvac.CarHvacManager.ID_WINDOW_DEFROSTER_ON
-android.car.hardware.hvac.CarHvacManager.FAN_DIRECTION_FACE
-android.car.hardware.hvac.CarHvacManager.FAN_DIRECTION_FLOOR
-android.car.hardware.hvac.CarHvacManager.FAN_DIRECTION_DEFROST
-android.car.hardware.hvac.CarHvacManager.registerCallback
-android.car.hardware.hvac.CarHvacManager.unregisterCallback
-android.car.hardware.hvac.CarHvacManager.getPropertyList
-android.car.hardware.hvac.CarHvacManager.isPropertyAvailable
-android.car.hardware.hvac.CarHvacManager.getBooleanProperty
-android.car.hardware.hvac.CarHvacManager.getFloatProperty
-android.car.hardware.hvac.CarHvacManager.getIntProperty
-android.car.hardware.hvac.CarHvacManager.setBooleanProperty
-android.car.hardware.hvac.CarHvacManager.setFloatProperty
-android.car.hardware.hvac.CarHvacManager.setIntProperty
-android.car.hardware.hvac.CarHvacManager.onCarDisconnected
-android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback.onChangeEvent
-android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback.onErrorEvent
-android.car.hardware.power.CarPowerManager.TAG
-android.car.hardware.power.CarPowerManager.STATE_INVALID
-android.car.hardware.power.CarPowerManager.STATE_WAIT_FOR_VHAL
-android.car.hardware.power.CarPowerManager.STATE_SUSPEND_ENTER
-android.car.hardware.power.CarPowerManager.STATE_SUSPEND_EXIT
-android.car.hardware.power.CarPowerManager.STATE_SHUTDOWN_ENTER
-android.car.hardware.power.CarPowerManager.STATE_ON
-android.car.hardware.power.CarPowerManager.STATE_SHUTDOWN_PREPARE
-android.car.hardware.power.CarPowerManager.STATE_SHUTDOWN_CANCELLED
-android.car.hardware.power.CarPowerManager.STATE_HIBERNATION_ENTER
-android.car.hardware.power.CarPowerManager.STATE_HIBERNATION_EXIT
-android.car.hardware.power.CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE
-android.car.hardware.power.CarPowerManager.STATE_POST_SUSPEND_ENTER
-android.car.hardware.power.CarPowerManager.STATE_POST_SHUTDOWN_ENTER
-android.car.hardware.power.CarPowerManager.STATE_POST_HIBERNATION_ENTER
-android.car.hardware.power.CarPowerManager.requestShutdownOnNextSuspend
-android.car.hardware.power.CarPowerManager.scheduleNextWakeupTime
-android.car.hardware.power.CarPowerManager.getPowerState
-android.car.hardware.power.CarPowerManager.setListener
-android.car.hardware.power.CarPowerManager.setListenerWithCompletion
-android.car.hardware.power.CarPowerManager.clearListener
-android.car.hardware.power.CarPowerManager.getCurrentPowerPolicy
-android.car.hardware.power.CarPowerManager.applyPowerPolicy
-android.car.hardware.power.CarPowerManager.setPowerPolicyGroup
-android.car.hardware.power.CarPowerManager.addPowerPolicyListener
-android.car.hardware.power.CarPowerManager.removePowerPolicyListener
-android.car.hardware.power.CarPowerManager.isCompletionAllowed
-android.car.hardware.power.CarPowerManager.onCarDisconnected
-android.car.hardware.power.CarPowerManager.CompletablePowerStateChangeFuture.complete
-android.car.hardware.power.CarPowerManager.CompletablePowerStateChangeFuture.getExpirationTime
-android.car.hardware.power.CarPowerManager.CarPowerStateListener.onStateChanged
-android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion.onStateChanged
-android.car.hardware.power.CarPowerManager.CarPowerPolicyListener.onPolicyChanged
-android.car.hardware.power.CarPowerPolicy.CREATOR
-android.car.hardware.power.CarPowerPolicy.isComponentEnabled
-android.car.hardware.power.CarPowerPolicy.getPolicyId
-android.car.hardware.power.CarPowerPolicy.getEnabledComponents
-android.car.hardware.power.CarPowerPolicy.getDisabledComponents
-android.car.hardware.power.CarPowerPolicy.writeToParcel
-android.car.hardware.power.CarPowerPolicy.describeContents
-android.car.hardware.power.CarPowerPolicyFilter.CREATOR
-android.car.hardware.power.CarPowerPolicyFilter.getComponents
-android.car.hardware.power.CarPowerPolicyFilter.writeToParcel
-android.car.hardware.power.CarPowerPolicyFilter.describeContents
-android.car.hardware.power.CarPowerPolicyFilter.Builder.setComponents
-android.car.hardware.power.CarPowerPolicyFilter.Builder.build
-android.car.hardware.power.PowerComponentUtil.COMPONENT_STATE_ENABLED
-android.car.hardware.power.PowerComponentUtil.COMPONENT_STATE_DISABLED
-android.car.hardware.power.PowerComponentUtil.COMPONENT_STATE_UNTOUCHED
-android.car.hardware.power.PowerComponentUtil.INVALID_POWER_COMPONENT
-android.car.hardware.power.PowerComponentUtil.FIRST_POWER_COMPONENT
-android.car.hardware.power.PowerComponentUtil.LAST_POWER_COMPONENT
-android.car.hardware.power.PowerComponentUtil.isValidPowerComponent
-android.car.hardware.power.PowerComponentUtil.hasComponents
-android.car.hardware.power.PowerComponentUtil.toPowerComponent
-android.car.hardware.power.PowerComponentUtil.powerComponentToString
-android.car.hardware.property.AreaIdConfig.getAreaId
-android.car.hardware.property.AreaIdConfig.getMinValue
-android.car.hardware.property.AreaIdConfig.getMaxValue
-android.car.hardware.property.AreaIdConfig.getSupportedEnumValues
-android.car.hardware.property.AreaIdConfig.describeContents
-android.car.hardware.property.AreaIdConfig.writeToParcel
-android.car.hardware.property.AreaIdConfig.toString
-android.car.hardware.property.AreaIdConfig.Builder.setMinValue
-android.car.hardware.property.AreaIdConfig.Builder.setMaxValue
-android.car.hardware.property.AreaIdConfig.Builder.setSupportedEnumValues
-android.car.hardware.property.AreaIdConfig.Builder.build
-android.car.hardware.property.CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE
-android.car.hardware.property.CarPropertyEvent.PROPERTY_EVENT_ERROR
-android.car.hardware.property.CarPropertyEvent.CREATOR
-android.car.hardware.property.CarPropertyEvent.getEventType
-android.car.hardware.property.CarPropertyEvent.getCarPropertyValue
-android.car.hardware.property.CarPropertyEvent.describeContents
-android.car.hardware.property.CarPropertyEvent.writeToParcel
-android.car.hardware.property.CarPropertyEvent.createErrorEventWithErrorCode
-android.car.hardware.property.CarPropertyEvent.getErrorCode
-android.car.hardware.property.CarPropertyEvent.toString
-android.car.hardware.property.CarPropertyEvent.equals
-android.car.hardware.property.CarPropertyEvent.hashCode
-android.car.hardware.property.CarPropertyManager.SENSOR_RATE_ONCHANGE
-android.car.hardware.property.CarPropertyManager.SENSOR_RATE_NORMAL
-android.car.hardware.property.CarPropertyManager.SENSOR_RATE_UI
-android.car.hardware.property.CarPropertyManager.SENSOR_RATE_FAST
-android.car.hardware.property.CarPropertyManager.SENSOR_RATE_FASTEST
-android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN
-android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG
-android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE
-android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED
-android.car.hardware.property.CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN
-android.car.hardware.property.CarPropertyManager.registerCallback
-android.car.hardware.property.CarPropertyManager.unregisterCallback
-android.car.hardware.property.CarPropertyManager.unregisterCallback
-android.car.hardware.property.CarPropertyManager.getPropertyList
-android.car.hardware.property.CarPropertyManager.getPropertyList
-android.car.hardware.property.CarPropertyManager.getCarPropertyConfig
-android.car.hardware.property.CarPropertyManager.getAreaId
-android.car.hardware.property.CarPropertyManager.getReadPermission
-android.car.hardware.property.CarPropertyManager.getWritePermission
-android.car.hardware.property.CarPropertyManager.isPropertyAvailable
-android.car.hardware.property.CarPropertyManager.getBooleanProperty
-android.car.hardware.property.CarPropertyManager.getFloatProperty
-android.car.hardware.property.CarPropertyManager.getIntProperty
-android.car.hardware.property.CarPropertyManager.getIntArrayProperty
-android.car.hardware.property.CarPropertyManager.getProperty
-android.car.hardware.property.CarPropertyManager.getProperty
-android.car.hardware.property.CarPropertyManager.setProperty
-android.car.hardware.property.CarPropertyManager.setBooleanProperty
-android.car.hardware.property.CarPropertyManager.setFloatProperty
-android.car.hardware.property.CarPropertyManager.setIntProperty
-android.car.hardware.property.CarPropertyManager.onCarDisconnected
-android.car.hardware.property.CarPropertyManager.generateGetPropertyRequest
-android.car.hardware.property.CarPropertyManager.getPropertiesAsync
-android.car.hardware.property.CarPropertyManager.getPropertiesAsync
-android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback.onChangeEvent
-android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback.onErrorEvent
-android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback.onErrorEvent
-android.car.hardware.property.CarPropertyManager.GetPropertyCallback.onSuccess
-android.car.hardware.property.CarPropertyManager.GetPropertyCallback.onFailure
-android.car.hardware.property.CarPropertyManager.GetPropertyRequest.getRequestId
-android.car.hardware.property.CarPropertyManager.GetPropertyRequest.getPropertyId
-android.car.hardware.property.CarPropertyManager.GetPropertyRequest.getAreaId
-android.car.hardware.property.CarPropertyManager.GetPropertyResult.getRequestId
-android.car.hardware.property.CarPropertyManager.GetPropertyResult.getCarPropertyValue
-android.car.hardware.property.CarPropertyManager.GetPropertyError.getRequestId
-android.car.hardware.property.CarPropertyManager.GetPropertyError.getErrorCode
-android.car.hardware.property.EvChargeState.toString
-android.car.hardware.property.EvChargingConnectorType.UNKNOWN
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_1_AC
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_2_AC
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_3_AC
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_4_DC
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_1_CCS_DC
-android.car.hardware.property.EvChargingConnectorType.IEC_TYPE_2_CCS_DC
-android.car.hardware.property.EvChargingConnectorType.TESLA_ROADSTER
-android.car.hardware.property.EvChargingConnectorType.TESLA_HPWC
-android.car.hardware.property.EvChargingConnectorType.TESLA_SUPERCHARGER
-android.car.hardware.property.EvChargingConnectorType.GBT_AC
-android.car.hardware.property.EvChargingConnectorType.GBT_DC
-android.car.hardware.property.EvChargingConnectorType.OTHER
-android.car.hardware.property.EvChargingConnectorType.toString
-android.car.hardware.property.EvRegenerativeBrakingState.toString
-android.car.hardware.property.GetPropertyServiceRequest.getRequestId
-android.car.hardware.property.GetPropertyServiceRequest.getPropertyId
-android.car.hardware.property.GetPropertyServiceRequest.getAreaId
-android.car.hardware.property.GetPropertyServiceRequest.writeToParcel
-android.car.hardware.property.GetPropertyServiceRequest.describeContents
-android.car.hardware.property.GetValueResult.getRequestId
-android.car.hardware.property.GetValueResult.getCarPropertyValue
-android.car.hardware.property.GetValueResult.getErrorCode
-android.car.hardware.property.GetValueResult.writeToParcel
-android.car.hardware.property.GetValueResult.describeContents
-android.car.hardware.property.VehicleElectronicTollCollectionCardStatus.UNKNOWN
-android.car.hardware.property.VehicleElectronicTollCollectionCardStatus.ELECTRONIC_TOLL_COLLECTION_CARD_VALID
-android.car.hardware.property.VehicleElectronicTollCollectionCardStatus.ELECTRONIC_TOLL_COLLECTION_CARD_INVALID
-android.car.hardware.property.VehicleElectronicTollCollectionCardStatus.ELECTRONIC_TOLL_COLLECTION_CARD_NOT_INSERTED
-android.car.hardware.property.VehicleElectronicTollCollectionCardType.UNKNOWN
-android.car.hardware.property.VehicleElectronicTollCollectionCardType.JP_ELECTRONIC_TOLL_COLLECTION_CARD
-android.car.hardware.property.VehicleElectronicTollCollectionCardType.JP_ELECTRONIC_TOLL_COLLECTION_CARD_V2
-android.car.hardware.property.VehicleHalStatusCode.STATUS_OK
-android.car.hardware.property.VehicleHalStatusCode.STATUS_TRY_AGAIN
-android.car.hardware.property.VehicleHalStatusCode.STATUS_INVALID_ARG
-android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE
-android.car.hardware.property.VehicleHalStatusCode.STATUS_ACCESS_DENIED
-android.car.hardware.property.VehicleHalStatusCode.STATUS_INTERNAL_ERROR
-android.car.hardware.property.VehicleLightState.toString
-android.car.hardware.property.VehicleLightSwitch.toString
-android.car.hardware.property.VehicleTurnSignal.toString
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_WINDOW
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_WINDOW
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_DOOR
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_DOOR
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_SEAT
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_SEAT
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_MIRROR
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_MIRROR
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_INFO
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_HVAC
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_HVAC
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_LIGHT
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_LIGHT
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_1
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_1
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_2
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_2
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_3
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_3
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_4
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_4
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_5
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_5
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_6
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_6
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_7
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_7
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_8
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_8
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_9
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_9
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_10
-android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_10
-android.car.input.CarInputHandlingService.INPUT_CALLBACK_BINDER_KEY
-android.car.input.CarInputHandlingService.INPUT_CALLBACK_BINDER_CODE
-android.car.input.CarInputHandlingService.onBind
-android.car.input.CarInputHandlingService.onKeyEvent
-android.car.input.CarInputHandlingService.dump
-android.car.input.CarInputHandlingService.InputFilter.mKeyCode
-android.car.input.CarInputHandlingService.InputFilter.mTargetDisplay
-android.car.input.CarInputHandlingService.InputFilter.CREATOR
-android.car.input.CarInputHandlingService.InputFilter.describeContents
-android.car.input.CarInputHandlingService.InputFilter.writeToParcel
-android.car.input.CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT
-android.car.input.CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY
-android.car.input.CarInputManager.INPUT_TYPE_ALL_INPUTS
-android.car.input.CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION
-android.car.input.CarInputManager.INPUT_TYPE_ROTARY_VOLUME
-android.car.input.CarInputManager.INPUT_TYPE_DPAD_KEYS
-android.car.input.CarInputManager.INPUT_TYPE_NAVIGATE_KEYS
-android.car.input.CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS
-android.car.input.CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT
-android.car.input.CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED
-android.car.input.CarInputManager.INPUT_CAPTURE_RESPONSE_FAILED
-android.car.input.CarInputManager.INPUT_CAPTURE_RESPONSE_DELAYED
-android.car.input.CarInputManager.requestInputEventCapture
-android.car.input.CarInputManager.requestInputEventCapture
-android.car.input.CarInputManager.releaseInputEventCapture
-android.car.input.CarInputManager.injectKeyEvent
-android.car.input.CarInputManager.onCarDisconnected
-android.car.input.CarInputManager.CarInputCaptureCallback.onKeyEvents
-android.car.input.CarInputManager.CarInputCaptureCallback.onRotaryEvents
-android.car.input.CarInputManager.CarInputCaptureCallback.onCaptureStateChanged
-android.car.input.CarInputManager.CarInputCaptureCallback.onCustomInputEvents
-android.car.input.CustomInputEvent.INPUT_CODE_F1
-android.car.input.CustomInputEvent.INPUT_CODE_F2
-android.car.input.CustomInputEvent.INPUT_CODE_F3
-android.car.input.CustomInputEvent.INPUT_CODE_F4
-android.car.input.CustomInputEvent.INPUT_CODE_F5
-android.car.input.CustomInputEvent.INPUT_CODE_F6
-android.car.input.CustomInputEvent.INPUT_CODE_F7
-android.car.input.CustomInputEvent.INPUT_CODE_F8
-android.car.input.CustomInputEvent.INPUT_CODE_F9
-android.car.input.CustomInputEvent.INPUT_CODE_F10
-android.car.input.CustomInputEvent.CREATOR
-android.car.input.CustomInputEvent.inputCodeToString
-android.car.input.CustomInputEvent.getInputCode
-android.car.input.CustomInputEvent.getTargetDisplayType
-android.car.input.CustomInputEvent.getRepeatCounter
-android.car.input.CustomInputEvent.toString
-android.car.input.CustomInputEvent.equals
-android.car.input.CustomInputEvent.hashCode
-android.car.input.CustomInputEvent.writeToParcel
-android.car.input.CustomInputEvent.describeContents
-android.car.input.RotaryEvent.CREATOR
-android.car.input.RotaryEvent.getNumberOfClicks
-android.car.input.RotaryEvent.getUptimeMillisForClick
-android.car.input.RotaryEvent.toString
-android.car.input.RotaryEvent.getInputType
-android.car.input.RotaryEvent.isClockwise
-android.car.input.RotaryEvent.getUptimeMillisForClicks
-android.car.input.RotaryEvent.equals
-android.car.input.RotaryEvent.hashCode
-android.car.input.RotaryEvent.writeToParcel
-android.car.input.RotaryEvent.describeContents
-android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE
-android.car.media.CarAudioManager.INVALID_AUDIO_ZONE
-android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING
-android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING
-android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID
-android.car.media.CarAudioManager.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS
-android.car.media.CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
-android.car.media.CarAudioManager.isDynamicRoutingEnabled
-android.car.media.CarAudioManager.isAudioFeatureEnabled
-android.car.media.CarAudioManager.setGroupVolume
-android.car.media.CarAudioManager.setGroupVolume
-android.car.media.CarAudioManager.getGroupMaxVolume
-android.car.media.CarAudioManager.getGroupMaxVolume
-android.car.media.CarAudioManager.getGroupMinVolume
-android.car.media.CarAudioManager.getGroupMinVolume
-android.car.media.CarAudioManager.getGroupVolume
-android.car.media.CarAudioManager.getGroupVolume
-android.car.media.CarAudioManager.setFadeTowardFront
-android.car.media.CarAudioManager.setBalanceTowardRight
-android.car.media.CarAudioManager.getExternalSources
-android.car.media.CarAudioManager.createAudioPatch
-android.car.media.CarAudioManager.releaseAudioPatch
-android.car.media.CarAudioManager.getVolumeGroupCount
-android.car.media.CarAudioManager.getVolumeGroupCount
-android.car.media.CarAudioManager.getVolumeGroupIdForUsage
-android.car.media.CarAudioManager.getVolumeGroupIdForUsage
-android.car.media.CarAudioManager.getUsagesForVolumeGroupId
-android.car.media.CarAudioManager.getVolumeGroupInfo
-android.car.media.CarAudioManager.getVolumeGroupInfosForZone
-android.car.media.CarAudioManager.getUsagesForVolumeGroupId
-android.car.media.CarAudioManager.isPlaybackOnVolumeGroupActive
-android.car.media.CarAudioManager.getAudioZoneIds
-android.car.media.CarAudioManager.getZoneIdForUid
-android.car.media.CarAudioManager.setZoneIdForUid
-android.car.media.CarAudioManager.clearZoneIdForUid
-android.car.media.CarAudioManager.getOutputDeviceForUsage
-android.car.media.CarAudioManager.getInputDevicesForZoneId
-android.car.media.CarAudioManager.onCarDisconnected
-android.car.media.CarAudioManager.registerCarVolumeCallback
-android.car.media.CarAudioManager.unregisterCarVolumeCallback
-android.car.media.CarAudioManager.isVolumeGroupMuted
-android.car.media.CarAudioManager.setVolumeGroupMute
-android.car.media.CarAudioManager.CarVolumeCallback.onGroupVolumeChanged
-android.car.media.CarAudioManager.CarVolumeCallback.onMasterMuteChanged
-android.car.media.CarAudioManager.CarVolumeCallback.onGroupMuteChanged
-android.car.media.CarAudioPatchHandle.CREATOR
-android.car.media.CarAudioPatchHandle.toString
-android.car.media.CarAudioPatchHandle.writeToParcel
-android.car.media.CarAudioPatchHandle.describeContents
-android.car.media.CarAudioPatchHandle.getSourceAddress
-android.car.media.CarAudioPatchHandle.getSinkAddress
-android.car.media.CarAudioPatchHandle.getHandleId
-android.car.media.CarMediaIntents.ACTION_MEDIA_TEMPLATE
-android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT
-android.car.media.CarMediaIntents.EXTRA_SEARCH_QUERY
-android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK
-android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE
-android.car.media.CarMediaManager.getMediaSource
-android.car.media.CarMediaManager.setMediaSource
-android.car.media.CarMediaManager.addMediaSourceListener
-android.car.media.CarMediaManager.removeMediaSourceListener
-android.car.media.CarMediaManager.getLastMediaSources
-android.car.media.CarMediaManager.onCarDisconnected
-android.car.media.CarMediaManager.isIndependentPlaybackConfig
-android.car.media.CarMediaManager.setIndependentPlaybackConfig
-android.car.media.CarMediaManager.MediaSourceChangedListener.onMediaSourceChanged
-android.car.media.CarVolumeGroupInfo.describeContents
-android.car.media.CarVolumeGroupInfo.getName
-android.car.media.CarVolumeGroupInfo.getZoneId
-android.car.media.CarVolumeGroupInfo.getId
-android.car.media.CarVolumeGroupInfo.getVolumeGain
-android.car.media.CarVolumeGroupInfo.isMuted
-android.car.media.CarVolumeGroupInfo.isBlocked
-android.car.media.CarVolumeGroupInfo.isAttenuated
-android.car.media.CarVolumeGroupInfo.toString
-android.car.media.CarVolumeGroupInfo.writeToParcel
-android.car.media.CarVolumeGroupInfo.isSameVolumeGroup
-android.car.media.CarVolumeGroupInfo.equals
-android.car.media.CarVolumeGroupInfo.hashCode
-android.car.media.CarVolumeGroupInfo.Builder.setVolumeGain
-android.car.media.CarVolumeGroupInfo.Builder.setMuted
-android.car.media.CarVolumeGroupInfo.Builder.setBlocked
-android.car.media.CarVolumeGroupInfo.Builder.setAttenuated
-android.car.media.CarVolumeGroupInfo.Builder.build
-android.car.navigation.CarNavigationInstrumentCluster.CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED
-android.car.navigation.CarNavigationInstrumentCluster.CLUSTER_TYPE_IMAGE_CODES_ONLY
-android.car.navigation.CarNavigationInstrumentCluster.CREATOR
-android.car.navigation.CarNavigationInstrumentCluster.createCluster
-android.car.navigation.CarNavigationInstrumentCluster.createCustomImageCluster
-android.car.navigation.CarNavigationInstrumentCluster.getMinIntervalMillis
-android.car.navigation.CarNavigationInstrumentCluster.getType
-android.car.navigation.CarNavigationInstrumentCluster.getImageWidth
-android.car.navigation.CarNavigationInstrumentCluster.getImageHeight
-android.car.navigation.CarNavigationInstrumentCluster.getExtra
-android.car.navigation.CarNavigationInstrumentCluster.getImageColorDepthBits
-android.car.navigation.CarNavigationInstrumentCluster.supportsCustomImages
-android.car.navigation.CarNavigationInstrumentCluster.describeContents
-android.car.navigation.CarNavigationInstrumentCluster.writeToParcel
-android.car.navigation.CarNavigationInstrumentCluster.toString
-android.car.navigation.CarNavigationStatusManager.sendEvent
-android.car.navigation.CarNavigationStatusManager.sendNavigationStateChange
-android.car.navigation.CarNavigationStatusManager.onCarDisconnected
-android.car.navigation.CarNavigationStatusManager.getInstrumentClusterInfo
-android.car.occupantawareness.DriverMonitoringDetection.confidenceLevel
-android.car.occupantawareness.DriverMonitoringDetection.isLookingOnRoad
-android.car.occupantawareness.DriverMonitoringDetection.gazeDurationMillis
-android.car.occupantawareness.DriverMonitoringDetection.CREATOR
-android.car.occupantawareness.DriverMonitoringDetection.describeContents
-android.car.occupantawareness.DriverMonitoringDetection.toString
-android.car.occupantawareness.DriverMonitoringDetection.writeToParcel
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_UNKNOWN
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_CENTER_INSTRUMENT_CLUSTER
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_REAR_VIEW_MIRROR
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_LEFT_SIDE_MIRROR
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_RIGHT_SIDE_MIRROR
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_FORWARD_ROADWAY
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_LEFT_ROADWAY
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_RIGHT_ROADWAY
-android.car.occupantawareness.GazeDetection.VEHICLE_REGION_HEAD_UNIT_DISPLAY
-android.car.occupantawareness.GazeDetection.confidenceLevel
-android.car.occupantawareness.GazeDetection.leftEyePosition
-android.car.occupantawareness.GazeDetection.rightEyePosition
-android.car.occupantawareness.GazeDetection.headAngleUnitVector
-android.car.occupantawareness.GazeDetection.gazeAngleUnitVector
-android.car.occupantawareness.GazeDetection.gazeTarget
-android.car.occupantawareness.GazeDetection.durationOnTargetMillis
-android.car.occupantawareness.GazeDetection.CREATOR
-android.car.occupantawareness.GazeDetection.describeContents
-android.car.occupantawareness.GazeDetection.writeToParcel
-android.car.occupantawareness.GazeDetection.toString
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_NONE
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_LEFT
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_CENTER
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_3_PASSENGER_LEFT
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_3_PASSENGER_CENTER
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_3_PASSENGER_RIGHT
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ALL_FRONT_OCCUPANTS
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ALL_ROW_2_OCCUPANTS
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ALL_ROW_3_OCCUPANTS
-android.car.occupantawareness.OccupantAwarenessDetection.VEHICLE_OCCUPANT_ALL_OCCUPANTS
-android.car.occupantawareness.OccupantAwarenessDetection.CONFIDENCE_LEVEL_NONE
-android.car.occupantawareness.OccupantAwarenessDetection.CONFIDENCE_LEVEL_LOW
-android.car.occupantawareness.OccupantAwarenessDetection.CONFIDENCE_LEVEL_HIGH
-android.car.occupantawareness.OccupantAwarenessDetection.CONFIDENCE_LEVEL_MAX
-android.car.occupantawareness.OccupantAwarenessDetection.role
-android.car.occupantawareness.OccupantAwarenessDetection.timestampMillis
-android.car.occupantawareness.OccupantAwarenessDetection.isPresent
-android.car.occupantawareness.OccupantAwarenessDetection.gazeDetection
-android.car.occupantawareness.OccupantAwarenessDetection.driverMonitoringDetection
-android.car.occupantawareness.OccupantAwarenessDetection.CREATOR
-android.car.occupantawareness.OccupantAwarenessDetection.describeContents
-android.car.occupantawareness.OccupantAwarenessDetection.writeToParcel
-android.car.occupantawareness.OccupantAwarenessDetection.toString
-android.car.occupantawareness.OccupantAwarenessManager.onCarDisconnected
-android.car.occupantawareness.OccupantAwarenessManager.getCapabilityForRole
-android.car.occupantawareness.OccupantAwarenessManager.registerChangeCallback
-android.car.occupantawareness.OccupantAwarenessManager.unregisterChangeCallback
-android.car.occupantawareness.OccupantAwarenessManager.ChangeCallback.onSystemStateChanged
-android.car.occupantawareness.OccupantAwarenessManager.ChangeCallback.onDetectionEvent
-android.car.occupantawareness.Point3D.x
-android.car.occupantawareness.Point3D.y
-android.car.occupantawareness.Point3D.z
-android.car.occupantawareness.Point3D.CREATOR
-android.car.occupantawareness.Point3D.describeContents
-android.car.occupantawareness.Point3D.writeToParcel
-android.car.occupantawareness.Point3D.toString
-android.car.occupantawareness.SystemStatusEvent.SYSTEM_STATUS_READY
-android.car.occupantawareness.SystemStatusEvent.SYSTEM_STATUS_NOT_SUPPORTED
-android.car.occupantawareness.SystemStatusEvent.SYSTEM_STATUS_NOT_READY
-android.car.occupantawareness.SystemStatusEvent.SYSTEM_STATUS_SYSTEM_FAILURE
-android.car.occupantawareness.SystemStatusEvent.DETECTION_TYPE_NONE
-android.car.occupantawareness.SystemStatusEvent.DETECTION_TYPE_PRESENCE
-android.car.occupantawareness.SystemStatusEvent.DETECTION_TYPE_GAZE
-android.car.occupantawareness.SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING
-android.car.occupantawareness.SystemStatusEvent.systemStatus
-android.car.occupantawareness.SystemStatusEvent.detectionType
-android.car.occupantawareness.SystemStatusEvent.CREATOR
-android.car.occupantawareness.SystemStatusEvent.describeContents
-android.car.occupantawareness.SystemStatusEvent.writeToParcel
-android.car.occupantawareness.SystemStatusEvent.toString
-android.car.oem.AudioFocusEntry.getAudioFocusInfo
-android.car.oem.AudioFocusEntry.getAudioContextId
-android.car.oem.AudioFocusEntry.getAudioVolumeGroupId
-android.car.oem.AudioFocusEntry.getAudioFocusResult
-android.car.oem.AudioFocusEntry.toString
-android.car.oem.AudioFocusEntry.writeToParcel
-android.car.oem.AudioFocusEntry.describeContents
-android.car.oem.AudioFocusEntry.equals
-android.car.oem.AudioFocusEntry.hashCode
-android.car.oem.AudioFocusEntry.Builder.setAudioFocusInfo
-android.car.oem.AudioFocusEntry.Builder.setAudioContextId
-android.car.oem.AudioFocusEntry.Builder.setAudioVolumeGroupId
-android.car.oem.AudioFocusEntry.Builder.setAudioFocusResult
-android.car.oem.AudioFocusEntry.Builder.build
-android.car.oem.OemCarAudioDuckingService.evaluateAttributesToDuck
-android.car.oem.OemCarAudioFocusEvaluationRequest.describeContents
-android.car.oem.OemCarAudioFocusEvaluationRequest.writeToParcel
-android.car.oem.OemCarAudioFocusEvaluationRequest.getAudioZoneId
-android.car.oem.OemCarAudioFocusEvaluationRequest.getAudioFocusRequest
-android.car.oem.OemCarAudioFocusEvaluationRequest.getMutedVolumeGroups
-android.car.oem.OemCarAudioFocusEvaluationRequest.getFocusHolders
-android.car.oem.OemCarAudioFocusEvaluationRequest.getFocusLosers
-android.car.oem.OemCarAudioFocusEvaluationRequest.equals
-android.car.oem.OemCarAudioFocusEvaluationRequest.hashCode
-android.car.oem.OemCarAudioFocusEvaluationRequest.toString
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.setAudioZoneId
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.setAudioFocusRequest
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.setMutedVolumeGroups
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.addMutedVolumeGroups
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.setFocusHolders
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.addFocusHolders
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.setFocusLosers
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.addFocusLosers
-android.car.oem.OemCarAudioFocusEvaluationRequest.Builder.build
-android.car.oem.OemCarAudioFocusResult.getAudioFocusEntry
-android.car.oem.OemCarAudioFocusResult.getNewlyLostAudioFocusEntries
-android.car.oem.OemCarAudioFocusResult.getNewlyBlockedAudioFocusEntries
-android.car.oem.OemCarAudioFocusResult.getAudioFocusResult
-android.car.oem.OemCarAudioFocusResult.toString
-android.car.oem.OemCarAudioFocusResult.writeToParcel
-android.car.oem.OemCarAudioFocusResult.describeContents
-android.car.oem.OemCarAudioFocusResult.equals
-android.car.oem.OemCarAudioFocusResult.hashCode
-android.car.oem.OemCarAudioFocusResult.Builder.setAudioFocusEntry
-android.car.oem.OemCarAudioFocusResult.Builder.setNewlyLostAudioFocusEntries
-android.car.oem.OemCarAudioFocusResult.Builder.addNewlyLostAudioFocusEntry
-android.car.oem.OemCarAudioFocusResult.Builder.setNewlyBlockedAudioFocusEntries
-android.car.oem.OemCarAudioFocusResult.Builder.addNewlyBlockedAudioFocusEntry
-android.car.oem.OemCarAudioFocusResult.Builder.setAudioFocusResult
-android.car.oem.OemCarAudioFocusResult.Builder.build
-android.car.oem.OemCarAudioFocusService.notifyAudioFocusChange
-android.car.oem.OemCarAudioFocusService.evaluateAudioFocusRequest
-android.car.oem.OemCarAudioVolumeRequest.getAudioZoneId
-android.car.oem.OemCarAudioVolumeRequest.getCallState
-android.car.oem.OemCarAudioVolumeRequest.getActivePlaybackAttributes
-android.car.oem.OemCarAudioVolumeRequest.getDuckedAudioAttributes
-android.car.oem.OemCarAudioVolumeRequest.getCarVolumeGroupInfos
-android.car.oem.OemCarAudioVolumeRequest.toString
-android.car.oem.OemCarAudioVolumeRequest.equals
-android.car.oem.OemCarAudioVolumeRequest.hashCode
-android.car.oem.OemCarAudioVolumeRequest.writeToParcel
-android.car.oem.OemCarAudioVolumeRequest.describeContents
-android.car.oem.OemCarAudioVolumeRequest.Builder.setCallState
-android.car.oem.OemCarAudioVolumeRequest.Builder.setActivePlaybackAttributes
-android.car.oem.OemCarAudioVolumeRequest.Builder.addActivePlaybackAttributes
-android.car.oem.OemCarAudioVolumeRequest.Builder.setDuckedAudioAttributes
-android.car.oem.OemCarAudioVolumeRequest.Builder.addDuckedAudioAttributes
-android.car.oem.OemCarAudioVolumeRequest.Builder.setCarVolumeGroupInfos
-android.car.oem.OemCarAudioVolumeRequest.Builder.addCarVolumeGroupInfos
-android.car.oem.OemCarAudioVolumeRequest.Builder.build
-android.car.oem.OemCarAudioVolumeService.getSuggestedGroupForVolumeChange
-android.car.oem.OemCarService.onCreate
-android.car.oem.OemCarService.onDestroy
-android.car.oem.OemCarService.onStartCommand
-android.car.oem.OemCarService.onBind
-android.car.oem.OemCarService.getOemAudioFocusService
-android.car.oem.OemCarService.getOemAudioVolumeService
-android.car.oem.OemCarService.getOemAudioDuckingService
-android.car.oem.OemCarService.dump
-android.car.oem.OemCarService.getSupportedCarVersion
-android.car.oem.OemCarService.onCarServiceReady
-android.car.oem.OemCarServiceComponent.init
-android.car.oem.OemCarServiceComponent.release
-android.car.oem.OemCarServiceComponent.dump
-android.car.oem.OemCarServiceComponent.onCarServiceReady
-android.car.oem.OemCarVolumeChangeInfo.isVolumeChanged
-android.car.oem.OemCarVolumeChangeInfo.getChangedVolumeGroup
-android.car.oem.OemCarVolumeChangeInfo.toString
-android.car.oem.OemCarVolumeChangeInfo.equals
-android.car.oem.OemCarVolumeChangeInfo.hashCode
-android.car.oem.OemCarVolumeChangeInfo.writeToParcel
-android.car.oem.OemCarVolumeChangeInfo.describeContents
-android.car.oem.OemCarVolumeChangeInfo.Builder.setChangedVolumeGroup
-android.car.oem.OemCarVolumeChangeInfo.Builder.build
-android.car.os.CarPerformanceManager.onCarDisconnected
-android.car.os.CarPerformanceManager.addCpuAvailabilityChangeListener
-android.car.os.CarPerformanceManager.removeCpuAvailabilityChangeListener
-android.car.os.CarPerformanceManager.setThreadPriority
-android.car.os.CarPerformanceManager.getThreadPriority
-android.car.os.CarPerformanceManager.CpuAvailabilityChangeListener.onCpuAvailabilityChange
-android.car.os.CpuAvailabilityInfo.CREATOR
-android.car.os.CpuAvailabilityInfo.getCpuset
-android.car.os.CpuAvailabilityInfo.getAverageAvailabilityPercent
-android.car.os.CpuAvailabilityInfo.isTimeout
-android.car.os.CpuAvailabilityInfo.toString
-android.car.os.CpuAvailabilityInfo.writeToParcel
-android.car.os.CpuAvailabilityInfo.describeContents
-android.car.os.CpuAvailabilityInfo.Builder.setCpuset
-android.car.os.CpuAvailabilityInfo.Builder.setAverageAvailabilityPercent
-android.car.os.CpuAvailabilityInfo.Builder.setTimeout
-android.car.os.CpuAvailabilityInfo.Builder.build
-android.car.os.CpuAvailabilityMonitoringConfig.CPUSET_ALL
-android.car.os.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND
-android.car.os.CpuAvailabilityMonitoringConfig.IGNORE_PERCENT_LOWER_BOUND
-android.car.os.CpuAvailabilityMonitoringConfig.IGNORE_PERCENT_UPPER_BOUND
-android.car.os.CpuAvailabilityMonitoringConfig.MONITORING_TIMEOUT_NEVER
-android.car.os.CpuAvailabilityMonitoringConfig.TIMEOUT_ACTION_NOTIFICATION
-android.car.os.CpuAvailabilityMonitoringConfig.TIMEOUT_ACTION_REMOVE
-android.car.os.CpuAvailabilityMonitoringConfig.CREATOR
-android.car.os.CpuAvailabilityMonitoringConfig.cpusetToString
-android.car.os.CpuAvailabilityMonitoringConfig.ignorePercentToString
-android.car.os.CpuAvailabilityMonitoringConfig.timeoutActionToString
-android.car.os.CpuAvailabilityMonitoringConfig.getCpuset
-android.car.os.CpuAvailabilityMonitoringConfig.getLowerBoundPercent
-android.car.os.CpuAvailabilityMonitoringConfig.getUpperBoundPercent
-android.car.os.CpuAvailabilityMonitoringConfig.getTimeoutInSeconds
-android.car.os.CpuAvailabilityMonitoringConfig.getTimeoutAction
-android.car.os.CpuAvailabilityMonitoringConfig.toString
-android.car.os.CpuAvailabilityMonitoringConfig.writeToParcel
-android.car.os.CpuAvailabilityMonitoringConfig.describeContents
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.setCpuset
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.setLowerBoundPercent
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.setUpperBoundPercent
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.setTimeoutInSeconds
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.setTimeoutAction
-android.car.os.CpuAvailabilityMonitoringConfig.Builder.build
-android.car.os.ThreadPolicyWithPriority.priorityToString
-android.car.os.ThreadPolicyWithPriority.schedToString
-android.car.os.ThreadPolicyWithPriority.getPolicy
-android.car.os.ThreadPolicyWithPriority.getPriority
-android.car.os.ThreadPolicyWithPriority.writeToParcel
-android.car.os.ThreadPolicyWithPriority.describeContents
-android.car.projection.ProjectionOptions.UI_MODE_FULL_SCREEN
-android.car.projection.ProjectionOptions.UI_MODE_BLENDED
-android.car.projection.ProjectionOptions.AP_MODE_NOT_SPECIFIED
-android.car.projection.ProjectionOptions.AP_MODE_TETHERED
-android.car.projection.ProjectionOptions.AP_MODE_LOHS_DYNAMIC_CREDENTIALS
-android.car.projection.ProjectionOptions.AP_MODE_LOHS_STATIC_CREDENTIALS
-android.car.projection.ProjectionOptions.getUiMode
-android.car.projection.ProjectionOptions.getProjectionAccessPointMode
-android.car.projection.ProjectionOptions.getActivityOptions
-android.car.projection.ProjectionOptions.getConsentActivity
-android.car.projection.ProjectionOptions.toBundle
-android.car.projection.ProjectionOptions.builder
-android.car.projection.ProjectionOptions.toString
-android.car.projection.ProjectionOptions.Builder.setProjectionActivityOptions
-android.car.projection.ProjectionOptions.Builder.setUiMode
-android.car.projection.ProjectionOptions.Builder.setConsentActivity
-android.car.projection.ProjectionOptions.Builder.setAccessPointMode
-android.car.projection.ProjectionOptions.Builder.build
-android.car.projection.ProjectionStatus.PROJECTION_STATE_INACTIVE
-android.car.projection.ProjectionStatus.PROJECTION_STATE_READY_TO_PROJECT
-android.car.projection.ProjectionStatus.PROJECTION_STATE_ACTIVE_FOREGROUND
-android.car.projection.ProjectionStatus.PROJECTION_STATE_ACTIVE_BACKGROUND
-android.car.projection.ProjectionStatus.PROJECTION_TRANSPORT_NONE
-android.car.projection.ProjectionStatus.PROJECTION_TRANSPORT_USB
-android.car.projection.ProjectionStatus.PROJECTION_TRANSPORT_WIFI
-android.car.projection.ProjectionStatus.CREATOR
-android.car.projection.ProjectionStatus.describeContents
-android.car.projection.ProjectionStatus.writeToParcel
-android.car.projection.ProjectionStatus.getState
-android.car.projection.ProjectionStatus.getPackageName
-android.car.projection.ProjectionStatus.getExtras
-android.car.projection.ProjectionStatus.isActive
-android.car.projection.ProjectionStatus.getTransport
-android.car.projection.ProjectionStatus.getConnectedMobileDevices
-android.car.projection.ProjectionStatus.builder
-android.car.projection.ProjectionStatus.toString
-android.car.projection.ProjectionStatus.Builder.setProjectionTransport
-android.car.projection.ProjectionStatus.Builder.addMobileDevice
-android.car.projection.ProjectionStatus.Builder.setExtras
-android.car.projection.ProjectionStatus.Builder.build
-android.car.projection.ProjectionStatus.MobileDevice.CREATOR
-android.car.projection.ProjectionStatus.MobileDevice.writeToParcel
-android.car.projection.ProjectionStatus.MobileDevice.getId
-android.car.projection.ProjectionStatus.MobileDevice.getName
-android.car.projection.ProjectionStatus.MobileDevice.getAvailableTransports
-android.car.projection.ProjectionStatus.MobileDevice.isProjecting
-android.car.projection.ProjectionStatus.MobileDevice.getExtras
-android.car.projection.ProjectionStatus.MobileDevice.describeContents
-android.car.projection.ProjectionStatus.MobileDevice.builder
-android.car.projection.ProjectionStatus.MobileDevice.toString
-android.car.projection.ProjectionStatus.MobileDevice.Builder.addTransport
-android.car.projection.ProjectionStatus.MobileDevice.Builder.setProjecting
-android.car.projection.ProjectionStatus.MobileDevice.Builder.setExtras
-android.car.projection.ProjectionStatus.MobileDevice.Builder.build
-android.car.remoteaccess.CarRemoteAccessManager.onCarDisconnected
-android.car.remoteaccess.CarRemoteAccessManager.setRemoteTaskClient
-android.car.remoteaccess.CarRemoteAccessManager.clearRemoteTaskClient
-android.car.remoteaccess.CarRemoteAccessManager.reportRemoteTaskDone
-android.car.remoteaccess.CarRemoteAccessManager.setPowerStatePostTaskExecution
-android.car.remoteaccess.CarRemoteAccessManager.CompletableRemoteTaskFuture.complete
-android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback.onRegistrationUpdated
-android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback.onRegistrationFailed
-android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback.onRemoteTaskRequested
-android.car.remoteaccess.CarRemoteAccessManager.RemoteTaskClientCallback.onShutdownStarting
-android.car.settings.CarSettings.DEFAULT_GARAGE_MODE_WAKE_UP_TIME
-android.car.settings.CarSettings.DEFAULT_GARAGE_MODE_MAINTENANCE_WINDOW
-android.car.settings.CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET
-android.car.settings.CarSettings.Global.DISABLE_INSTRUMENTATION_SERVICE
-android.car.settings.CarSettings.Global.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE
-android.car.settings.CarSettings.Global.LAST_ACTIVE_USER_ID
-android.car.settings.CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID
-android.car.settings.CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE
-android.car.settings.CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL
-android.car.settings.CarSettings.Secure.KEY_AUDIO_PERSIST_VOLUME_GROUP_MUTE_STATES
-android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_DEVICES
-android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_PROFILES_INHIBITED
-android.car.settings.CarSettings.Secure.KEY_ROTARY_KEY_EVENT_FILTER
-android.car.settings.CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER
-android.car.settings.CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS
-android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE
-android.car.storagemonitoring.CarStorageMonitoringManager.INTENT_EXCESSIVE_IO
-android.car.storagemonitoring.CarStorageMonitoringManager.PRE_EOL_INFO_UNKNOWN
-android.car.storagemonitoring.CarStorageMonitoringManager.PRE_EOL_INFO_NORMAL
-android.car.storagemonitoring.CarStorageMonitoringManager.PRE_EOL_INFO_WARNING
-android.car.storagemonitoring.CarStorageMonitoringManager.PRE_EOL_INFO_URGENT
-android.car.storagemonitoring.CarStorageMonitoringManager.SHUTDOWN_COST_INFO_MISSING
-android.car.storagemonitoring.CarStorageMonitoringManager.onCarDisconnected
-android.car.storagemonitoring.CarStorageMonitoringManager.getPreEolIndicatorStatus
-android.car.storagemonitoring.CarStorageMonitoringManager.getWearEstimate
-android.car.storagemonitoring.CarStorageMonitoringManager.getWearEstimateHistory
-android.car.storagemonitoring.CarStorageMonitoringManager.getBootIoStats
-android.car.storagemonitoring.CarStorageMonitoringManager.getShutdownDiskWriteAmount
-android.car.storagemonitoring.CarStorageMonitoringManager.getAggregateIoStats
-android.car.storagemonitoring.CarStorageMonitoringManager.getIoStatsDeltas
-android.car.storagemonitoring.CarStorageMonitoringManager.registerListener
-android.car.storagemonitoring.CarStorageMonitoringManager.unregisterListener
-android.car.storagemonitoring.CarStorageMonitoringManager.IoStatsListener.onSnapshot
-android.car.storagemonitoring.IoStats.CREATOR
-android.car.storagemonitoring.IoStats.writeToParcel
-android.car.storagemonitoring.IoStats.writeToJson
-android.car.storagemonitoring.IoStats.describeContents
-android.car.storagemonitoring.IoStats.getTimestamp
-android.car.storagemonitoring.IoStats.getStats
-android.car.storagemonitoring.IoStats.hashCode
-android.car.storagemonitoring.IoStats.getUserIdStats
-android.car.storagemonitoring.IoStats.getForegroundTotals
-android.car.storagemonitoring.IoStats.getBackgroundTotals
-android.car.storagemonitoring.IoStats.getTotals
-android.car.storagemonitoring.IoStats.equals
-android.car.storagemonitoring.IoStats.toString
-android.car.storagemonitoring.IoStatsEntry.CREATOR
-android.car.storagemonitoring.IoStatsEntry.uid
-android.car.storagemonitoring.IoStatsEntry.runtimeMillis
-android.car.storagemonitoring.IoStatsEntry.foreground
-android.car.storagemonitoring.IoStatsEntry.background
-android.car.storagemonitoring.IoStatsEntry.describeContents
-android.car.storagemonitoring.IoStatsEntry.writeToParcel
-android.car.storagemonitoring.IoStatsEntry.writeToJson
-android.car.storagemonitoring.IoStatsEntry.delta
-android.car.storagemonitoring.IoStatsEntry.equals
-android.car.storagemonitoring.IoStatsEntry.hashCode
-android.car.storagemonitoring.IoStatsEntry.toString
-android.car.storagemonitoring.IoStatsEntry.representsSameMetrics
-android.car.storagemonitoring.IoStatsEntry.Metrics.CREATOR
-android.car.storagemonitoring.IoStatsEntry.Metrics.bytesRead
-android.car.storagemonitoring.IoStatsEntry.Metrics.bytesWritten
-android.car.storagemonitoring.IoStatsEntry.Metrics.bytesReadFromStorage
-android.car.storagemonitoring.IoStatsEntry.Metrics.bytesWrittenToStorage
-android.car.storagemonitoring.IoStatsEntry.Metrics.fsyncCalls
-android.car.storagemonitoring.IoStatsEntry.Metrics.describeContents
-android.car.storagemonitoring.IoStatsEntry.Metrics.writeToParcel
-android.car.storagemonitoring.IoStatsEntry.Metrics.writeToJson
-android.car.storagemonitoring.IoStatsEntry.Metrics.delta
-android.car.storagemonitoring.IoStatsEntry.Metrics.equals
-android.car.storagemonitoring.IoStatsEntry.Metrics.hashCode
-android.car.storagemonitoring.IoStatsEntry.Metrics.toString
-android.car.storagemonitoring.LifetimeWriteInfo.CREATOR
-android.car.storagemonitoring.LifetimeWriteInfo.partition
-android.car.storagemonitoring.LifetimeWriteInfo.fstype
-android.car.storagemonitoring.LifetimeWriteInfo.writtenBytes
-android.car.storagemonitoring.LifetimeWriteInfo.writeToParcel
-android.car.storagemonitoring.LifetimeWriteInfo.writeToJson
-android.car.storagemonitoring.LifetimeWriteInfo.describeContents
-android.car.storagemonitoring.LifetimeWriteInfo.equals
-android.car.storagemonitoring.LifetimeWriteInfo.hashCode
-android.car.storagemonitoring.LifetimeWriteInfo.toString
-android.car.storagemonitoring.UidIoRecord.uid
-android.car.storagemonitoring.UidIoRecord.foreground_rchar
-android.car.storagemonitoring.UidIoRecord.foreground_wchar
-android.car.storagemonitoring.UidIoRecord.foreground_read_bytes
-android.car.storagemonitoring.UidIoRecord.foreground_write_bytes
-android.car.storagemonitoring.UidIoRecord.foreground_fsync
-android.car.storagemonitoring.UidIoRecord.background_rchar
-android.car.storagemonitoring.UidIoRecord.background_wchar
-android.car.storagemonitoring.UidIoRecord.background_read_bytes
-android.car.storagemonitoring.UidIoRecord.background_write_bytes
-android.car.storagemonitoring.UidIoRecord.background_fsync
-android.car.storagemonitoring.UidIoRecord.delta
-android.car.storagemonitoring.UidIoRecord.delta
-android.car.storagemonitoring.WearEstimate.UNKNOWN
-android.car.storagemonitoring.WearEstimate.UNKNOWN_ESTIMATE
-android.car.storagemonitoring.WearEstimate.CREATOR
-android.car.storagemonitoring.WearEstimate.typeA
-android.car.storagemonitoring.WearEstimate.typeB
-android.car.storagemonitoring.WearEstimate.describeContents
-android.car.storagemonitoring.WearEstimate.writeToParcel
-android.car.storagemonitoring.WearEstimate.writeToJson
-android.car.storagemonitoring.WearEstimate.equals
-android.car.storagemonitoring.WearEstimate.hashCode
-android.car.storagemonitoring.WearEstimate.toString
-android.car.storagemonitoring.WearEstimateChange.CREATOR
-android.car.storagemonitoring.WearEstimateChange.oldEstimate
-android.car.storagemonitoring.WearEstimateChange.newEstimate
-android.car.storagemonitoring.WearEstimateChange.uptimeAtChange
-android.car.storagemonitoring.WearEstimateChange.dateAtChange
-android.car.storagemonitoring.WearEstimateChange.isAcceptableDegradation
-android.car.storagemonitoring.WearEstimateChange.describeContents
-android.car.storagemonitoring.WearEstimateChange.writeToParcel
-android.car.storagemonitoring.WearEstimateChange.equals
-android.car.storagemonitoring.WearEstimateChange.hashCode
-android.car.storagemonitoring.WearEstimateChange.toString
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_SUCCEEDED
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_ALREADY_EXISTS
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_VERSION_TOO_OLD
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_PARSE_FAILED
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_SIGNATURE_VERIFICATION_FAILED
-android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_UNKNOWN
-android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_FINISHED
-android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_PENDING
-android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_INTERIM_RESULTS
-android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_RUNTIME_ERROR
-android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_DOES_NOT_EXIST
-android.car.telemetry.CarTelemetryManager.onCarDisconnected
-android.car.telemetry.CarTelemetryManager.addMetricsConfig
-android.car.telemetry.CarTelemetryManager.removeMetricsConfig
-android.car.telemetry.CarTelemetryManager.removeAllMetricsConfigs
-android.car.telemetry.CarTelemetryManager.getFinishedReport
-android.car.telemetry.CarTelemetryManager.getAllFinishedReports
-android.car.telemetry.CarTelemetryManager.setReportReadyListener
-android.car.telemetry.CarTelemetryManager.clearReportReadyListener
-android.car.telemetry.CarTelemetryManager.AddMetricsConfigCallback.onAddMetricsConfigStatus
-android.car.telemetry.CarTelemetryManager.MetricsReportCallback.onResult
-android.car.telemetry.CarTelemetryManager.ReportReadyListener.onReady
-android.car.test.CarLocationTestHelper.injectLocation
-android.car.test.CarTestManager.onCarDisconnected
-android.car.test.CarTestManager.stopCarService
-android.car.test.CarTestManager.startCarService
-android.car.test.CarTestManager.dumpVhal
-android.car.test.CarTestManager.hasAidlVhal
-android.car.test.CarTestManager.getOemServiceName
-android.car.user.CarUserManager.TAG
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPING
-android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED
-android.car.user.CarUserManager.BUNDLE_PARAM_ACTION
-android.car.user.CarUserManager.BUNDLE_PARAM_PREVIOUS_USER_ID
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_TYPE_KEY_FOB
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_1
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_2
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_3
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_4
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_ASSOCIATE_CURRENT_USER
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_CURRENT_USER
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_ALL_USERS
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_VALUE_UNKNOWN
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATE_CURRENT_USER
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATED_ANOTHER_USER
-android.car.user.CarUserManager.USER_IDENTIFICATION_ASSOCIATION_VALUE_NOT_ASSOCIATED_ANY_USER
-android.car.user.CarUserManager.switchUser
-android.car.user.CarUserManager.logoutUser
-android.car.user.CarUserManager.createGuest
-android.car.user.CarUserManager.createUser
-android.car.user.CarUserManager.updatePreCreatedUsers
-android.car.user.CarUserManager.removeUser
-android.car.user.CarUserManager.addListener
-android.car.user.CarUserManager.addListener
-android.car.user.CarUserManager.removeListener
-android.car.user.CarUserManager.isUserHalUserAssociationSupported
-android.car.user.CarUserManager.getUserIdentificationAssociation
-android.car.user.CarUserManager.setUserIdentificationAssociation
-android.car.user.CarUserManager.setUserSwitchUiCallback
-android.car.user.CarUserManager.onCarDisconnected
-android.car.user.CarUserManager.lifecycleEventTypeToString
-android.car.user.CarUserManager.isValidUser
-android.car.user.CarUserManager.UserLifecycleEvent.getEventType
-android.car.user.CarUserManager.UserLifecycleEvent.getUserId
-android.car.user.CarUserManager.UserLifecycleEvent.getUserHandle
-android.car.user.CarUserManager.UserLifecycleEvent.getPreviousUserId
-android.car.user.CarUserManager.UserLifecycleEvent.getPreviousUserHandle
-android.car.user.CarUserManager.UserLifecycleEvent.toString
-android.car.user.CarUserManager.UserLifecycleEvent.equals
-android.car.user.CarUserManager.UserLifecycleEvent.hashCode
-android.car.user.CarUserManager.UserLifecycleListener.onEvent
-android.car.user.CarUserManager.UserSwitchUiCallback.showUserSwitchDialog
-android.car.user.ExperimentalCarUserManager.TAG
-android.car.user.ExperimentalCarUserManager.createDriver
-android.car.user.ExperimentalCarUserManager.createPassenger
-android.car.user.ExperimentalCarUserManager.switchDriver
-android.car.user.ExperimentalCarUserManager.getAllDrivers
-android.car.user.ExperimentalCarUserManager.getPassengers
-android.car.user.ExperimentalCarUserManager.startPassenger
-android.car.user.ExperimentalCarUserManager.stopPassenger
-android.car.user.ExperimentalCarUserManager.onCarDisconnected
-android.car.user.OperationResult.isSuccess
-android.car.user.UserCreationResult.STATUS_SUCCESSFUL
-android.car.user.UserCreationResult.STATUS_ANDROID_FAILURE
-android.car.user.UserCreationResult.STATUS_HAL_FAILURE
-android.car.user.UserCreationResult.STATUS_HAL_INTERNAL_FAILURE
-android.car.user.UserCreationResult.STATUS_INVALID_REQUEST
-android.car.user.UserCreationResult.CREATOR
-android.car.user.UserCreationResult.isSuccess
-android.car.user.UserCreationResult.describeContents
-android.car.user.UserCreationResult.statusToString
-android.car.user.UserCreationResult.getStatus
-android.car.user.UserCreationResult.getAndroidFailureStatus
-android.car.user.UserCreationResult.getUser
-android.car.user.UserCreationResult.getErrorMessage
-android.car.user.UserCreationResult.getInternalErrorMessage
-android.car.user.UserCreationResult.toString
-android.car.user.UserCreationResult.writeToParcel
-android.car.user.UserIdentificationAssociationResponse.CREATOR
-android.car.user.UserIdentificationAssociationResponse.forFailure
-android.car.user.UserIdentificationAssociationResponse.forFailure
-android.car.user.UserIdentificationAssociationResponse.forSuccess
-android.car.user.UserIdentificationAssociationResponse.forSuccess
-android.car.user.UserIdentificationAssociationResponse.isSuccess
-android.car.user.UserIdentificationAssociationResponse.getErrorMessage
-android.car.user.UserIdentificationAssociationResponse.getValues
-android.car.user.UserIdentificationAssociationResponse.toString
-android.car.user.UserIdentificationAssociationResponse.writeToParcel
-android.car.user.UserIdentificationAssociationResponse.describeContents
-android.car.user.UserLifecycleEventFilter.CREATOR
-android.car.user.UserLifecycleEventFilter.getEventTypes
-android.car.user.UserLifecycleEventFilter.getUserIds
-android.car.user.UserLifecycleEventFilter.apply
-android.car.user.UserLifecycleEventFilter.toString
-android.car.user.UserLifecycleEventFilter.equals
-android.car.user.UserLifecycleEventFilter.hashCode
-android.car.user.UserLifecycleEventFilter.writeToParcel
-android.car.user.UserLifecycleEventFilter.describeContents
-android.car.user.UserLifecycleEventFilter.Builder.addEventType
-android.car.user.UserLifecycleEventFilter.Builder.addUser
-android.car.user.UserLifecycleEventFilter.Builder.build
-android.car.user.UserRemovalResult.STATUS_SUCCESSFUL
-android.car.user.UserRemovalResult.STATUS_ANDROID_FAILURE
-android.car.user.UserRemovalResult.STATUS_INVALID_REQUEST
-android.car.user.UserRemovalResult.STATUS_USER_DOES_NOT_EXIST
-android.car.user.UserRemovalResult.STATUS_SUCCESSFUL_LAST_ADMIN_REMOVED
-android.car.user.UserRemovalResult.STATUS_SUCCESSFUL_SET_EPHEMERAL
-android.car.user.UserRemovalResult.STATUS_SUCCESSFUL_LAST_ADMIN_SET_EPHEMERAL
-android.car.user.UserRemovalResult.CREATOR
-android.car.user.UserRemovalResult.isSuccess
-android.car.user.UserRemovalResult.statusToString
-android.car.user.UserRemovalResult.getStatus
-android.car.user.UserRemovalResult.toString
-android.car.user.UserRemovalResult.writeToParcel
-android.car.user.UserRemovalResult.describeContents
-android.car.user.UserStartResult.STATUS_SUCCESSFUL
-android.car.user.UserStartResult.STATUS_ANDROID_FAILURE
-android.car.user.UserStartResult.STATUS_SUCCESSFUL_USER_IS_CURRENT_USER
-android.car.user.UserStartResult.STATUS_USER_DOES_NOT_EXIST
-android.car.user.UserStartResult.CREATOR
-android.car.user.UserStartResult.isSuccess
-android.car.user.UserStartResult.statusToString
-android.car.user.UserStartResult.getStatus
-android.car.user.UserStartResult.toString
-android.car.user.UserStartResult.writeToParcel
-android.car.user.UserStartResult.describeContents
-android.car.user.UserStopResult.STATUS_SUCCESSFUL
-android.car.user.UserStopResult.STATUS_ANDROID_FAILURE
-android.car.user.UserStopResult.STATUS_USER_DOES_NOT_EXIST
-android.car.user.UserStopResult.STATUS_FAILURE_SYSTEM_USER
-android.car.user.UserStopResult.STATUS_FAILURE_CURRENT_USER
-android.car.user.UserStopResult.CREATOR
-android.car.user.UserStopResult.isSuccess
-android.car.user.UserStopResult.isSuccess
-android.car.user.UserStopResult.statusToString
-android.car.user.UserStopResult.getStatus
-android.car.user.UserStopResult.toString
-android.car.user.UserStopResult.writeToParcel
-android.car.user.UserStopResult.describeContents
-android.car.user.UserSwitchResult.STATUS_SUCCESSFUL
-android.car.user.UserSwitchResult.STATUS_ANDROID_FAILURE
-android.car.user.UserSwitchResult.STATUS_HAL_FAILURE
-android.car.user.UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE
-android.car.user.UserSwitchResult.STATUS_INVALID_REQUEST
-android.car.user.UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE
-android.car.user.UserSwitchResult.STATUS_OK_USER_ALREADY_IN_FOREGROUND
-android.car.user.UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO
-android.car.user.UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST
-android.car.user.UserSwitchResult.STATUS_NOT_SWITCHABLE
-android.car.user.UserSwitchResult.STATUS_NOT_LOGGED_IN
-android.car.user.UserSwitchResult.CREATOR
-android.car.user.UserSwitchResult.isSuccess
-android.car.user.UserSwitchResult.describeContents
-android.car.user.UserSwitchResult.statusToString
-android.car.user.UserSwitchResult.getStatus
-android.car.user.UserSwitchResult.getAndroidFailureStatus
-android.car.user.UserSwitchResult.getErrorMessage
-android.car.user.UserSwitchResult.toString
-android.car.user.UserSwitchResult.writeToParcel
-android.car.util.concurrent.AndroidAsyncFuture.get
-android.car.util.concurrent.AndroidAsyncFuture.get
-android.car.util.concurrent.AndroidAsyncFuture.whenCompleteAsync
-android.car.util.concurrent.AndroidFuture.CREATOR
-android.car.util.concurrent.AndroidFuture.completedFuture
-android.car.util.concurrent.AndroidFuture.complete
-android.car.util.concurrent.AndroidFuture.completeExceptionally
-android.car.util.concurrent.AndroidFuture.cancel
-android.car.util.concurrent.AndroidFuture.onCompleted
-android.car.util.concurrent.AndroidFuture.whenComplete
-android.car.util.concurrent.AndroidFuture.whenCompleteAsync
-android.car.util.concurrent.AndroidFuture.orTimeout
-android.car.util.concurrent.AndroidFuture.cancelTimeout
-android.car.util.concurrent.AndroidFuture.setTimeoutHandler
-android.car.util.concurrent.AndroidFuture.thenCompose
-android.car.util.concurrent.AndroidFuture.thenComposeAsync
-android.car.util.concurrent.AndroidFuture.thenApply
-android.car.util.concurrent.AndroidFuture.thenApplyAsync
-android.car.util.concurrent.AndroidFuture.thenCombine
-android.car.util.concurrent.AndroidFuture.thenCombine
-android.car.util.concurrent.AndroidFuture.supply
-android.car.util.concurrent.AndroidFuture.supplyAsync
-android.car.util.concurrent.AndroidFuture.writeToParcel
-android.car.util.concurrent.AndroidFuture.describeContents
-android.car.util.concurrent.AsyncFuture.get
-android.car.util.concurrent.AsyncFuture.get
-android.car.util.concurrent.AsyncFuture.whenCompleteAsync
-android.car.vms.VmsAssociatedLayer.CREATOR
-android.car.vms.VmsAssociatedLayer.getPublisherIds
-android.car.vms.VmsAssociatedLayer.getVmsLayer
-android.car.vms.VmsAssociatedLayer.getProviderIds
-android.car.vms.VmsAssociatedLayer.toString
-android.car.vms.VmsAssociatedLayer.equals
-android.car.vms.VmsAssociatedLayer.hashCode
-android.car.vms.VmsAssociatedLayer.writeToParcel
-android.car.vms.VmsAssociatedLayer.describeContents
-android.car.vms.VmsAvailableLayers.CREATOR
-android.car.vms.VmsAvailableLayers.getSequence
-android.car.vms.VmsAvailableLayers.getSequenceNumber
-android.car.vms.VmsAvailableLayers.getAssociatedLayers
-android.car.vms.VmsAvailableLayers.toString
-android.car.vms.VmsAvailableLayers.equals
-android.car.vms.VmsAvailableLayers.hashCode
-android.car.vms.VmsAvailableLayers.writeToParcel
-android.car.vms.VmsAvailableLayers.describeContents
-android.car.vms.VmsClient.getProviderDescription
-android.car.vms.VmsClient.setSubscriptions
-android.car.vms.VmsClient.setMonitoringEnabled
-android.car.vms.VmsClient.isMonitoringEnabled
-android.car.vms.VmsClient.getAvailableLayers
-android.car.vms.VmsClient.registerProvider
-android.car.vms.VmsClient.unregisterProvider
-android.car.vms.VmsClient.setProviderOfferings
-android.car.vms.VmsClient.publishPacket
-android.car.vms.VmsClient.getSubscriptionState
-android.car.vms.VmsClient.register
-android.car.vms.VmsClient.unregister
-android.car.vms.VmsClientManager.registerVmsClientCallback
-android.car.vms.VmsClientManager.unregisterVmsClientCallback
-android.car.vms.VmsClientManager.onCarDisconnected
-android.car.vms.VmsClientManager.VmsClientCallback.onClientConnected
-android.car.vms.VmsClientManager.VmsClientCallback.onLayerAvailabilityChanged
-android.car.vms.VmsClientManager.VmsClientCallback.onSubscriptionStateChanged
-android.car.vms.VmsClientManager.VmsClientCallback.onPacketReceived
-android.car.vms.VmsLayer.CREATOR
-android.car.vms.VmsLayer.getSubtype
-android.car.vms.VmsLayer.getType
-android.car.vms.VmsLayer.getChannel
-android.car.vms.VmsLayer.getVersion
-android.car.vms.VmsLayer.toString
-android.car.vms.VmsLayer.equals
-android.car.vms.VmsLayer.hashCode
-android.car.vms.VmsLayer.writeToParcel
-android.car.vms.VmsLayer.describeContents
-android.car.vms.VmsLayerDependency.CREATOR
-android.car.vms.VmsLayerDependency.getLayer
-android.car.vms.VmsLayerDependency.getDependencies
-android.car.vms.VmsLayerDependency.toString
-android.car.vms.VmsLayerDependency.equals
-android.car.vms.VmsLayerDependency.hashCode
-android.car.vms.VmsLayerDependency.writeToParcel
-android.car.vms.VmsLayerDependency.describeContents
-android.car.vms.VmsLayersOffering.CREATOR
-android.car.vms.VmsLayersOffering.getDependencies
-android.car.vms.VmsLayersOffering.getPublisherId
-android.car.vms.VmsLayersOffering.toString
-android.car.vms.VmsLayersOffering.equals
-android.car.vms.VmsLayersOffering.hashCode
-android.car.vms.VmsLayersOffering.writeToParcel
-android.car.vms.VmsLayersOffering.describeContents
-android.car.vms.VmsOperationRecorder.get
-android.car.vms.VmsOperationRecorder.subscribe
-android.car.vms.VmsOperationRecorder.unsubscribe
-android.car.vms.VmsOperationRecorder.subscribe
-android.car.vms.VmsOperationRecorder.unsubscribe
-android.car.vms.VmsOperationRecorder.startMonitoring
-android.car.vms.VmsOperationRecorder.stopMonitoring
-android.car.vms.VmsOperationRecorder.setLayersOffering
-android.car.vms.VmsOperationRecorder.getPublisherId
-android.car.vms.VmsOperationRecorder.addSubscription
-android.car.vms.VmsOperationRecorder.removeSubscription
-android.car.vms.VmsOperationRecorder.addPromiscuousSubscription
-android.car.vms.VmsOperationRecorder.removePromiscuousSubscription
-android.car.vms.VmsOperationRecorder.addHalSubscription
-android.car.vms.VmsOperationRecorder.removeHalSubscription
-android.car.vms.VmsOperationRecorder.setPublisherLayersOffering
-android.car.vms.VmsOperationRecorder.setHalPublisherLayersOffering
-android.car.vms.VmsOperationRecorder.Writer.isEnabled
-android.car.vms.VmsOperationRecorder.Writer.write
-android.car.vms.VmsProviderInfo.CREATOR
-android.car.vms.VmsProviderInfo.getDescription
-android.car.vms.VmsProviderInfo.equals
-android.car.vms.VmsProviderInfo.hashCode
-android.car.vms.VmsProviderInfo.writeToParcel
-android.car.vms.VmsProviderInfo.describeContents
-android.car.vms.VmsPublisherClientService.onCreate
-android.car.vms.VmsPublisherClientService.onDestroy
-android.car.vms.VmsPublisherClientService.onBind
-android.car.vms.VmsPublisherClientService.onCarLifecycleChanged
-android.car.vms.VmsPublisherClientService.onVmsPublisherServiceReady
-android.car.vms.VmsPublisherClientService.onVmsSubscriptionChange
-android.car.vms.VmsPublisherClientService.publish
-android.car.vms.VmsPublisherClientService.setLayersOffering
-android.car.vms.VmsPublisherClientService.getPublisherId
-android.car.vms.VmsPublisherClientService.getSubscriptions
-android.car.vms.VmsRegistrationInfo.CREATOR
-android.car.vms.VmsRegistrationInfo.getAvailableLayers
-android.car.vms.VmsRegistrationInfo.getSubscriptionState
-android.car.vms.VmsRegistrationInfo.equals
-android.car.vms.VmsRegistrationInfo.hashCode
-android.car.vms.VmsRegistrationInfo.writeToParcel
-android.car.vms.VmsRegistrationInfo.describeContents
-android.car.vms.VmsSubscriberManager.wrap
-android.car.vms.VmsSubscriberManager.setVmsSubscriberClientCallback
-android.car.vms.VmsSubscriberManager.clearVmsSubscriberClientCallback
-android.car.vms.VmsSubscriberManager.getPublisherInfo
-android.car.vms.VmsSubscriberManager.getAvailableLayers
-android.car.vms.VmsSubscriberManager.subscribe
-android.car.vms.VmsSubscriberManager.subscribe
-android.car.vms.VmsSubscriberManager.startMonitoring
-android.car.vms.VmsSubscriberManager.unsubscribe
-android.car.vms.VmsSubscriberManager.unsubscribe
-android.car.vms.VmsSubscriberManager.stopMonitoring
-android.car.vms.VmsSubscriberManager.onCarDisconnected
-android.car.vms.VmsSubscriberManager.VmsSubscriberClientCallback.onVmsMessageReceived
-android.car.vms.VmsSubscriberManager.VmsSubscriberClientCallback.onLayersAvailabilityChanged
-android.car.vms.VmsSubscriptionHelper.subscribe
-android.car.vms.VmsSubscriptionHelper.subscribe
-android.car.vms.VmsSubscriptionHelper.unsubscribe
-android.car.vms.VmsSubscriptionHelper.unsubscribe
-android.car.vms.VmsSubscriptionHelper.getSubscriptions
-android.car.vms.VmsSubscriptionState.CREATOR
-android.car.vms.VmsSubscriptionState.getSequenceNumber
-android.car.vms.VmsSubscriptionState.getLayers
-android.car.vms.VmsSubscriptionState.getAssociatedLayers
-android.car.vms.VmsSubscriptionState.toString
-android.car.vms.VmsSubscriptionState.equals
-android.car.vms.VmsSubscriptionState.hashCode
-android.car.vms.VmsSubscriptionState.writeToParcel
-android.car.vms.VmsSubscriptionState.describeContents
-android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL
-android.car.watchdog.CarWatchdogManager.TIMEOUT_MODERATE
-android.car.watchdog.CarWatchdogManager.TIMEOUT_NORMAL
-android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY
-android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_3_DAYS
-android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS
-android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_15_DAYS
-android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_30_DAYS
-android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO
-android.car.watchdog.CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB
-android.car.watchdog.CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB
-android.car.watchdog.CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB
-android.car.watchdog.CarWatchdogManager.RETURN_CODE_SUCCESS
-android.car.watchdog.CarWatchdogManager.RETURN_CODE_ERROR
-android.car.watchdog.CarWatchdogManager.registerClient
-android.car.watchdog.CarWatchdogManager.unregisterClient
-android.car.watchdog.CarWatchdogManager.tellClientAlive
-android.car.watchdog.CarWatchdogManager.getResourceOveruseStats
-android.car.watchdog.CarWatchdogManager.getAllResourceOveruseStats
-android.car.watchdog.CarWatchdogManager.getResourceOveruseStatsForUserPackage
-android.car.watchdog.CarWatchdogManager.addResourceOveruseListener
-android.car.watchdog.CarWatchdogManager.removeResourceOveruseListener
-android.car.watchdog.CarWatchdogManager.addResourceOveruseListenerForSystem
-android.car.watchdog.CarWatchdogManager.removeResourceOveruseListenerForSystem
-android.car.watchdog.CarWatchdogManager.setKillablePackageAsUser
-android.car.watchdog.CarWatchdogManager.getPackageKillableStatesAsUser
-android.car.watchdog.CarWatchdogManager.setResourceOveruseConfigurations
-android.car.watchdog.CarWatchdogManager.getResourceOveruseConfigurations
-android.car.watchdog.CarWatchdogManager.onCarDisconnected
-android.car.watchdog.CarWatchdogManager.CarWatchdogClientCallback.onCheckHealthStatus
-android.car.watchdog.CarWatchdogManager.CarWatchdogClientCallback.onPrepareProcessTermination
-android.car.watchdog.CarWatchdogManager.ResourceOveruseListener.onOveruse
-android.car.watchdog.IoOveruseAlertThreshold.CREATOR
-android.car.watchdog.IoOveruseAlertThreshold.getDurationInSeconds
-android.car.watchdog.IoOveruseAlertThreshold.getWrittenBytesPerSecond
-android.car.watchdog.IoOveruseAlertThreshold.toString
-android.car.watchdog.IoOveruseAlertThreshold.writeToParcel
-android.car.watchdog.IoOveruseAlertThreshold.describeContents
-android.car.watchdog.IoOveruseConfiguration.CREATOR
-android.car.watchdog.IoOveruseConfiguration.getComponentLevelThresholds
-android.car.watchdog.IoOveruseConfiguration.getPackageSpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.getAppCategorySpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.getSystemWideThresholds
-android.car.watchdog.IoOveruseConfiguration.toString
-android.car.watchdog.IoOveruseConfiguration.writeToParcel
-android.car.watchdog.IoOveruseConfiguration.describeContents
-android.car.watchdog.IoOveruseConfiguration.Builder.setComponentLevelThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.setPackageSpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.addPackageSpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.setAppCategorySpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.addAppCategorySpecificThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.setSystemWideThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.addSystemWideThresholds
-android.car.watchdog.IoOveruseConfiguration.Builder.build
-android.car.watchdog.IoOveruseStats.CREATOR
-android.car.watchdog.IoOveruseStats.getStartTime
-android.car.watchdog.IoOveruseStats.getDurationInSeconds
-android.car.watchdog.IoOveruseStats.getTotalOveruses
-android.car.watchdog.IoOveruseStats.getTotalTimesKilled
-android.car.watchdog.IoOveruseStats.getTotalBytesWritten
-android.car.watchdog.IoOveruseStats.isKillableOnOveruse
-android.car.watchdog.IoOveruseStats.getRemainingWriteBytes
-android.car.watchdog.IoOveruseStats.toString
-android.car.watchdog.IoOveruseStats.writeToParcel
-android.car.watchdog.IoOveruseStats.describeContents
-android.car.watchdog.IoOveruseStats.Builder.setStartTime
-android.car.watchdog.IoOveruseStats.Builder.setDurationInSeconds
-android.car.watchdog.IoOveruseStats.Builder.setTotalOveruses
-android.car.watchdog.IoOveruseStats.Builder.setTotalTimesKilled
-android.car.watchdog.IoOveruseStats.Builder.setTotalBytesWritten
-android.car.watchdog.IoOveruseStats.Builder.setKillableOnOveruse
-android.car.watchdog.IoOveruseStats.Builder.setRemainingWriteBytes
-android.car.watchdog.IoOveruseStats.Builder.build
-android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES
-android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO
-android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER
-android.car.watchdog.PackageKillableState.CREATOR
-android.car.watchdog.PackageKillableState.killableStateToString
-android.car.watchdog.PackageKillableState.getPackageName
-android.car.watchdog.PackageKillableState.getUserId
-android.car.watchdog.PackageKillableState.getKillableState
-android.car.watchdog.PackageKillableState.toString
-android.car.watchdog.PackageKillableState.writeToParcel
-android.car.watchdog.PackageKillableState.describeContents
-android.car.watchdog.PerStateBytes.CREATOR
-android.car.watchdog.PerStateBytes.getForegroundModeBytes
-android.car.watchdog.PerStateBytes.getBackgroundModeBytes
-android.car.watchdog.PerStateBytes.getGarageModeBytes
-android.car.watchdog.PerStateBytes.toString
-android.car.watchdog.PerStateBytes.writeToParcel
-android.car.watchdog.PerStateBytes.describeContents
-android.car.watchdog.ResourceOveruseConfiguration.COMPONENT_TYPE_SYSTEM
-android.car.watchdog.ResourceOveruseConfiguration.COMPONENT_TYPE_VENDOR
-android.car.watchdog.ResourceOveruseConfiguration.COMPONENT_TYPE_THIRD_PARTY
-android.car.watchdog.ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS
-android.car.watchdog.ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA
-android.car.watchdog.ResourceOveruseConfiguration.CREATOR
-android.car.watchdog.ResourceOveruseConfiguration.componentTypeToString
-android.car.watchdog.ResourceOveruseConfiguration.getComponentType
-android.car.watchdog.ResourceOveruseConfiguration.getSafeToKillPackages
-android.car.watchdog.ResourceOveruseConfiguration.getVendorPackagePrefixes
-android.car.watchdog.ResourceOveruseConfiguration.getPackagesToAppCategoryTypes
-android.car.watchdog.ResourceOveruseConfiguration.getIoOveruseConfiguration
-android.car.watchdog.ResourceOveruseConfiguration.toString
-android.car.watchdog.ResourceOveruseConfiguration.writeToParcel
-android.car.watchdog.ResourceOveruseConfiguration.describeContents
-android.car.watchdog.ResourceOveruseConfiguration.Builder.setComponentType
-android.car.watchdog.ResourceOveruseConfiguration.Builder.setSafeToKillPackages
-android.car.watchdog.ResourceOveruseConfiguration.Builder.addSafeToKillPackages
-android.car.watchdog.ResourceOveruseConfiguration.Builder.setVendorPackagePrefixes
-android.car.watchdog.ResourceOveruseConfiguration.Builder.addVendorPackagePrefixes
-android.car.watchdog.ResourceOveruseConfiguration.Builder.setPackagesToAppCategoryTypes
-android.car.watchdog.ResourceOveruseConfiguration.Builder.addPackagesToAppCategoryTypes
-android.car.watchdog.ResourceOveruseConfiguration.Builder.setIoOveruseConfiguration
-android.car.watchdog.ResourceOveruseConfiguration.Builder.build
-android.car.watchdog.ResourceOveruseStats.CREATOR
-android.car.watchdog.ResourceOveruseStats.getPackageName
-android.car.watchdog.ResourceOveruseStats.getUserHandle
-android.car.watchdog.ResourceOveruseStats.getIoOveruseStats
-android.car.watchdog.ResourceOveruseStats.toString
-android.car.watchdog.ResourceOveruseStats.writeToParcel
-android.car.watchdog.ResourceOveruseStats.describeContents
-android.car.watchdog.ResourceOveruseStats.Builder.setPackageName
-android.car.watchdog.ResourceOveruseStats.Builder.setUserHandle
-android.car.watchdog.ResourceOveruseStats.Builder.setIoOveruseStats
-android.car.watchdog.ResourceOveruseStats.Builder.build
diff --git a/tests/carservice_unit_test/res/raw/car_api_classes.txt b/tests/carservice_unit_test/res/raw/car_api_classes.txt
deleted file mode 100644
index 4ee4084..0000000
--- a/tests/carservice_unit_test/res/raw/car_api_classes.txt
+++ /dev/null
@@ -1,353 +0,0 @@
-android.car.AoapService
-android.car.ApiVersion
-android.car.Car
-android.car.Car$CarServiceLifecycleListener
-android.car.CarAppFocusManager
-android.car.CarAppFocusManager$OnAppFocusChangedListener
-android.car.CarAppFocusManager$OnAppFocusOwnershipCallback
-android.car.CarBugreportManager
-android.car.CarBugreportManager$CarBugreportManagerCallback
-android.car.CarFeatures
-android.car.CarInfoManager
-android.car.CarLibLog
-android.car.CarManagerBase
-android.car.CarNotConnectedException
-android.car.CarOccupantZoneManager
-android.car.CarOccupantZoneManager$OccupantZoneInfo
-android.car.CarOccupantZoneManager$OccupantZoneConfigChangeListener
-android.car.CarProjectionManager
-android.car.CarProjectionManager$CarProjectionListener
-android.car.CarProjectionManager$ProjectionKeyEventHandler
-android.car.CarProjectionManager$ProjectionStatusListener
-android.car.CarProjectionManager$ProjectionAccessPointCallback
-android.car.CarRemoteDeviceManager
-android.car.CarRemoteDeviceManager$StateCallback
-android.car.CarTransactionException
-android.car.CarVersion
-android.car.CarVersion$VERSION_CODES
-android.car.EvConnectorType
-android.car.FuelType
-android.car.GsrComplianceType
-android.car.PlatformVersion
-android.car.PlatformVersion$VERSION_CODES
-android.car.PlatformVersionMismatchException
-android.car.PortLocationType
-android.car.ResultCallback
-android.car.SyncResultCallback
-android.car.VehicleAreaDoor
-android.car.VehicleAreaMirror
-android.car.VehicleAreaSeat
-android.car.VehicleAreaType
-android.car.VehicleAreaWheel
-android.car.VehicleAreaWindow
-android.car.VehicleGear
-android.car.VehicleHvacFanDirection
-android.car.VehicleIgnitionState
-android.car.VehicleLightState
-android.car.VehicleLightSwitch
-android.car.VehicleOilLevel
-android.car.VehiclePropertyAccess
-android.car.VehiclePropertyIds
-android.car.VehiclePropertyType
-android.car.VehicleSeatOccupancyState
-android.car.VehicleUnit
-android.car.admin.CarDevicePolicyManager
-android.car.admin.CreateUserResult
-android.car.admin.RemoveUserResult
-android.car.admin.StartUserInBackgroundResult
-android.car.admin.StopUserResult
-android.car.app.CarActivityManager
-android.car.app.CarSystemUIProxy
-android.car.app.CarTaskViewClient
-android.car.app.CarTaskViewController
-android.car.app.CarTaskViewControllerCallback
-android.car.app.CarTaskViewHost
-android.car.app.ControlledRemoteCarTaskView
-android.car.app.ControlledRemoteCarTaskViewCallback
-android.car.app.ControlledRemoteCarTaskViewConfig
-android.car.app.ControlledRemoteCarTaskViewConfig$Builder
-android.car.cluster.CarInstrumentClusterManager
-android.car.cluster.CarInstrumentClusterManager$Callback
-android.car.cluster.ClusterActivityState
-android.car.cluster.ClusterHomeManager
-android.car.cluster.ClusterHomeManager$ClusterStateListener
-android.car.cluster.ClusterHomeManager$ClusterNavigationStateListener
-android.car.cluster.renderer.InstrumentClusterRenderer
-android.car.cluster.renderer.InstrumentClusterRenderingService
-android.car.cluster.renderer.NavigationRenderer
-android.car.content.pm.AppBlockingPackageInfo
-android.car.content.pm.CarAppBlockingPolicy
-android.car.content.pm.CarAppBlockingPolicyService
-android.car.content.pm.CarPackageManager
-android.car.diagnostic.CarDiagnosticEvent
-android.car.diagnostic.CarDiagnosticEvent$Builder
-android.car.diagnostic.CarDiagnosticEvent$FuelSystemStatus
-android.car.diagnostic.CarDiagnosticEvent$SecondaryAirStatus
-android.car.diagnostic.CarDiagnosticEvent$FuelType
-android.car.diagnostic.CarDiagnosticEvent$IgnitionMonitor
-android.car.diagnostic.CarDiagnosticEvent$IgnitionMonitor$Decoder
-android.car.diagnostic.CarDiagnosticEvent$CommonIgnitionMonitors
-android.car.diagnostic.CarDiagnosticEvent$SparkIgnitionMonitors
-android.car.diagnostic.CarDiagnosticEvent$CompressionIgnitionMonitors
-android.car.diagnostic.CarDiagnosticManager
-android.car.diagnostic.CarDiagnosticManager$OnDiagnosticEventListener
-android.car.diagnostic.FloatSensorIndex
-android.car.diagnostic.IntegerSensorIndex
-android.car.drivingstate.CarDrivingStateEvent
-android.car.drivingstate.CarDrivingStateManager
-android.car.drivingstate.CarDrivingStateManager$CarDrivingStateEventListener
-android.car.drivingstate.CarUxRestrictions
-android.car.drivingstate.CarUxRestrictions$Builder
-android.car.drivingstate.CarUxRestrictionsConfiguration
-android.car.drivingstate.CarUxRestrictionsConfiguration$Builder
-android.car.drivingstate.CarUxRestrictionsConfiguration$Builder$SpeedRange
-android.car.drivingstate.CarUxRestrictionsConfiguration$DrivingStateRestrictions
-android.car.drivingstate.CarUxRestrictionsManager
-android.car.drivingstate.CarUxRestrictionsManager$OnUxRestrictionsChangedListener
-android.car.evs.CarEvsBufferDescriptor
-android.car.evs.CarEvsManager
-android.car.evs.CarEvsManager$CarEvsStatusListener
-android.car.evs.CarEvsManager$CarEvsStreamCallback
-android.car.evs.CarEvsStatus
-android.car.hardware.CarHvacFanDirection
-android.car.hardware.CarPropertyConfig
-android.car.hardware.CarPropertyConfig$AreaConfig
-android.car.hardware.CarPropertyConfig$Builder
-android.car.hardware.CarPropertyValue
-android.car.hardware.CarSensorConfig
-android.car.hardware.CarSensorEvent
-android.car.hardware.CarSensorEvent$EnvironmentData
-android.car.hardware.CarSensorEvent$IgnitionStateData
-android.car.hardware.CarSensorEvent$NightData
-android.car.hardware.CarSensorEvent$GearData
-android.car.hardware.CarSensorEvent$ParkingBrakeData
-android.car.hardware.CarSensorEvent$FuelLevelData
-android.car.hardware.CarSensorEvent$OdometerData
-android.car.hardware.CarSensorEvent$RpmData
-android.car.hardware.CarSensorEvent$CarSpeedData
-android.car.hardware.CarSensorEvent$CarWheelTickDistanceData
-android.car.hardware.CarSensorEvent$CarAbsActiveData
-android.car.hardware.CarSensorEvent$CarTractionControlActiveData
-android.car.hardware.CarSensorEvent$CarFuelDoorOpenData
-android.car.hardware.CarSensorEvent$CarEvBatteryLevelData
-android.car.hardware.CarSensorEvent$CarEvChargePortOpenData
-android.car.hardware.CarSensorEvent$CarEvChargePortConnectedData
-android.car.hardware.CarSensorEvent$CarEvBatteryChargeRateData
-android.car.hardware.CarSensorEvent$CarEngineOilLevelData
-android.car.hardware.CarSensorManager
-android.car.hardware.CarSensorManager$OnSensorChangedListener
-android.car.hardware.CarVendorExtensionManager
-android.car.hardware.CarVendorExtensionManager$CarVendorExtensionCallback
-android.car.hardware.cabin.CarCabinManager
-android.car.hardware.cabin.CarCabinManager$CarCabinEventCallback
-android.car.hardware.hvac.CarHvacManager
-android.car.hardware.hvac.CarHvacManager$CarHvacEventCallback
-android.car.hardware.power.CarPowerManager
-android.car.hardware.power.CarPowerManager$CompletablePowerStateChangeFuture
-android.car.hardware.power.CarPowerManager$CarPowerStateListener
-android.car.hardware.power.CarPowerManager$CarPowerStateListenerWithCompletion
-android.car.hardware.power.CarPowerManager$CarPowerPolicyListener
-android.car.hardware.power.CarPowerPolicy
-android.car.hardware.power.CarPowerPolicyFilter
-android.car.hardware.power.CarPowerPolicyFilter$Builder
-android.car.hardware.power.PowerComponentUtil
-android.car.hardware.property.AreaIdConfig
-android.car.hardware.property.AreaIdConfig$Builder
-android.car.hardware.property.AutomaticEmergencyBrakingState
-android.car.hardware.property.BlindSpotWarningState
-android.car.hardware.property.CarInternalErrorException
-android.car.hardware.property.CarPropertyEvent
-android.car.hardware.property.CarPropertyManager
-android.car.hardware.property.CarPropertyManager$CarPropertyEventCallback
-android.car.hardware.property.CarPropertyManager$GetPropertyCallback
-android.car.hardware.property.CarPropertyManager$SetPropertyCallback
-android.car.hardware.property.CarPropertyManager$AsyncPropertyRequest
-android.car.hardware.property.CarPropertyManager$GetPropertyRequest
-android.car.hardware.property.CarPropertyManager$SetPropertyRequest
-android.car.hardware.property.CarPropertyManager$PropertyAsyncError
-android.car.hardware.property.CarPropertyManager$GetPropertyResult
-android.car.hardware.property.CarPropertyManager$SetPropertyResult
-android.car.hardware.property.CruiseControlCommand
-android.car.hardware.property.CruiseControlState
-android.car.hardware.property.CruiseControlType
-android.car.hardware.property.EmergencyLaneKeepAssistState
-android.car.hardware.property.ErrorState
-android.car.hardware.property.EvChargeState
-android.car.hardware.property.EvChargingConnectorType
-android.car.hardware.property.EvRegenerativeBrakingState
-android.car.hardware.property.EvStoppingMode
-android.car.hardware.property.ForwardCollisionWarningState
-android.car.hardware.property.HandsOnDetectionDriverState
-android.car.hardware.property.HandsOnDetectionWarning
-android.car.hardware.property.LaneCenteringAssistCommand
-android.car.hardware.property.LaneCenteringAssistState
-android.car.hardware.property.LaneDepartureWarningState
-android.car.hardware.property.LaneKeepAssistState
-android.car.hardware.property.LocationCharacterization
-android.car.hardware.property.PropertyAccessDeniedSecurityException
-android.car.hardware.property.PropertyNotAvailableAndRetryException
-android.car.hardware.property.PropertyNotAvailableErrorCode
-android.car.hardware.property.PropertyNotAvailableException
-android.car.hardware.property.TrailerState
-android.car.hardware.property.VehicleElectronicTollCollectionCardStatus
-android.car.hardware.property.VehicleElectronicTollCollectionCardType
-android.car.hardware.property.VehicleHalStatusCode
-android.car.hardware.property.VehicleLightState
-android.car.hardware.property.VehicleLightSwitch
-android.car.hardware.property.VehicleOilLevel
-android.car.hardware.property.VehicleTurnSignal
-android.car.hardware.property.VehicleVendorPermission
-android.car.hardware.property.WindshieldWipersState
-android.car.hardware.property.WindshieldWipersSwitch
-android.car.input.CarInputHandlingService
-android.car.input.CarInputHandlingService$InputFilter
-android.car.input.CarInputManager
-android.car.input.CarInputManager$CarInputCaptureCallback
-android.car.input.CustomInputEvent
-android.car.input.RotaryEvent
-android.car.media.AudioZonesMirrorStatusCallback
-android.car.media.CarAudioManager
-android.car.media.CarAudioManager$CarVolumeCallback
-android.car.media.CarAudioPatchHandle
-android.car.media.CarAudioZoneConfigInfo
-android.car.media.CarMediaIntents
-android.car.media.CarMediaManager
-android.car.media.CarMediaManager$MediaSourceChangedListener
-android.car.media.CarVolumeGroupEvent
-android.car.media.CarVolumeGroupEvent$Builder
-android.car.media.CarVolumeGroupEventCallback
-android.car.media.CarVolumeGroupInfo
-android.car.media.CarVolumeGroupInfo$Builder
-android.car.media.MediaAudioRequestStatusCallback
-android.car.media.PrimaryZoneMediaAudioRequestCallback
-android.car.media.SwitchAudioZoneConfigCallback
-android.car.navigation.CarNavigationInstrumentCluster
-android.car.navigation.CarNavigationStatusManager
-android.car.occupantawareness.DriverMonitoringDetection
-android.car.occupantawareness.GazeDetection
-android.car.occupantawareness.OccupantAwarenessDetection
-android.car.occupantawareness.OccupantAwarenessManager
-android.car.occupantawareness.OccupantAwarenessManager$ChangeCallback
-android.car.occupantawareness.Point3D
-android.car.occupantawareness.SystemStatusEvent
-android.car.occupantconnection.AbstractReceiverService
-android.car.occupantconnection.CarOccupantConnectionManager
-android.car.occupantconnection.CarOccupantConnectionManager$ConnectionRequestCallback
-android.car.occupantconnection.CarOccupantConnectionManager$PayloadCallback
-android.car.occupantconnection.CarOccupantConnectionManager$PayloadTransferException
-android.car.occupantconnection.Payload
-android.car.oem.AudioFocusEntry
-android.car.oem.AudioFocusEntry$Builder
-android.car.oem.OemCarAudioDuckingService
-android.car.oem.OemCarAudioFocusEvaluationRequest
-android.car.oem.OemCarAudioFocusEvaluationRequest$Builder
-android.car.oem.OemCarAudioFocusResult
-android.car.oem.OemCarAudioFocusResult$Builder
-android.car.oem.OemCarAudioFocusService
-android.car.oem.OemCarAudioVolumeRequest
-android.car.oem.OemCarAudioVolumeRequest$Builder
-android.car.oem.OemCarAudioVolumeService
-android.car.oem.OemCarService
-android.car.oem.OemCarServiceComponent
-android.car.oem.OemCarVolumeChangeInfo
-android.car.oem.OemCarVolumeChangeInfo$Builder
-android.car.os.CarPerformanceManager
-android.car.os.CarPerformanceManager$SetSchedulerFailedException
-android.car.os.CarPerformanceManager$CpuAvailabilityChangeListener
-android.car.os.CpuAvailabilityInfo
-android.car.os.CpuAvailabilityInfo$Builder
-android.car.os.CpuAvailabilityMonitoringConfig
-android.car.os.CpuAvailabilityMonitoringConfig$Builder
-android.car.os.ThreadPolicyWithPriority
-android.car.projection.ProjectionOptions
-android.car.projection.ProjectionOptions$Builder
-android.car.projection.ProjectionStatus
-android.car.projection.ProjectionStatus$Builder
-android.car.projection.ProjectionStatus$MobileDevice
-android.car.projection.ProjectionStatus$MobileDevice$Builder
-android.car.remoteaccess.CarRemoteAccessManager
-android.car.remoteaccess.CarRemoteAccessManager$CompletableRemoteTaskFuture
-android.car.remoteaccess.CarRemoteAccessManager$RemoteTaskClientCallback
-android.car.remoteaccess.RemoteTaskClientRegistrationInfo
-android.car.settings.CarSettings
-android.car.settings.CarSettings$Global
-android.car.settings.CarSettings$Secure
-android.car.storagemonitoring.CarStorageMonitoringManager
-android.car.storagemonitoring.CarStorageMonitoringManager$IoStatsListener
-android.car.storagemonitoring.IoStats
-android.car.storagemonitoring.IoStatsEntry
-android.car.storagemonitoring.IoStatsEntry$Metrics
-android.car.storagemonitoring.LifetimeWriteInfo
-android.car.storagemonitoring.UidIoRecord
-android.car.storagemonitoring.WearEstimate
-android.car.storagemonitoring.WearEstimateChange
-android.car.telemetry.CarTelemetryManager
-android.car.telemetry.CarTelemetryManager$AddMetricsConfigCallback
-android.car.telemetry.CarTelemetryManager$MetricsReportCallback
-android.car.telemetry.CarTelemetryManager$ReportReadyListener
-android.car.test.CarLocationTestHelper
-android.car.test.CarTestManager
-android.car.user.CarUserManager
-android.car.user.CarUserManager$UserLifecycleEvent
-android.car.user.CarUserManager$UserLifecycleListener
-android.car.user.CarUserManager$UserSwitchUiCallback
-android.car.user.CarUserManager$UserHandleSwitchUiCallback
-android.car.user.ExperimentalCarUserManager
-android.car.user.OperationResult
-android.car.user.UserCreationRequest
-android.car.user.UserCreationRequest$Builder
-android.car.user.UserCreationResult
-android.car.user.UserIdentificationAssociationResponse
-android.car.user.UserLifecycleEventFilter
-android.car.user.UserLifecycleEventFilter$Builder
-android.car.user.UserRemovalRequest
-android.car.user.UserRemovalRequest$Builder
-android.car.user.UserRemovalResult
-android.car.user.UserStartRequest
-android.car.user.UserStartRequest$Builder
-android.car.user.UserStartResponse
-android.car.user.UserStartResult
-android.car.user.UserStopRequest
-android.car.user.UserStopRequest$Builder
-android.car.user.UserStopResponse
-android.car.user.UserStopResult
-android.car.user.UserSwitchRequest
-android.car.user.UserSwitchRequest$Builder
-android.car.user.UserSwitchResult
-android.car.util.concurrent.AndroidAsyncFuture
-android.car.util.concurrent.AndroidFuture
-android.car.util.concurrent.AsyncFuture
-android.car.view.DisplayHelper
-android.car.view.MirroredSurfaceView
-android.car.view.WindowManagerHelper
-android.car.vms.VmsAssociatedLayer
-android.car.vms.VmsAvailableLayers
-android.car.vms.VmsClient
-android.car.vms.VmsClientManager
-android.car.vms.VmsClientManager$VmsClientCallback
-android.car.vms.VmsLayer
-android.car.vms.VmsLayerDependency
-android.car.vms.VmsLayersOffering
-android.car.vms.VmsOperationRecorder
-android.car.vms.VmsOperationRecorder$Writer
-android.car.vms.VmsProviderInfo
-android.car.vms.VmsPublisherClientService
-android.car.vms.VmsRegistrationInfo
-android.car.vms.VmsSubscriberManager
-android.car.vms.VmsSubscriberManager$VmsSubscriberClientCallback
-android.car.vms.VmsSubscriptionHelper
-android.car.vms.VmsSubscriptionState
-android.car.watchdog.CarWatchdogManager
-android.car.watchdog.CarWatchdogManager$CarWatchdogClientCallback
-android.car.watchdog.CarWatchdogManager$ResourceOveruseListener
-android.car.watchdog.IoOveruseAlertThreshold
-android.car.watchdog.IoOveruseConfiguration
-android.car.watchdog.IoOveruseConfiguration$Builder
-android.car.watchdog.IoOveruseStats
-android.car.watchdog.IoOveruseStats$Builder
-android.car.watchdog.PackageKillableState
-android.car.watchdog.PerStateBytes
-android.car.watchdog.ResourceOveruseConfiguration
-android.car.watchdog.ResourceOveruseConfiguration$Builder
-android.car.watchdog.ResourceOveruseStats
-android.car.watchdog.ResourceOveruseStats$Builder
diff --git a/tests/carservice_unit_test/res/raw/car_audio_configuration_using_oem_defined_context.xml b/tests/carservice_unit_test/res/raw/car_audio_configuration_using_oem_defined_context.xml
new file mode 100644
index 0000000..7d76596
--- /dev/null
+++ b/tests/carservice_unit_test/res/raw/car_audio_configuration_using_oem_defined_context.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" ?>
+<!-- Copyright (C) 2023 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.
+-->
+<carAudioConfiguration version="3">
+ <oemContexts>
+ <oemContext name="OEM_MUSIC">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_UNKNOWN" />
+ <usage value="AUDIO_USAGE_MEDIA" />
+ <usage value="AUDIO_USAGE_GAME" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_NAVIGATION">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_VOICE_COMMAND">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+ <usage value="AUDIO_USAGE_ASSISTANT" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_CALL_RING">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_CALL">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+ <usage value="AUDIO_USAGE_CALL_ASSISTANT" />
+ <usage value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_ALARM">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ALARM" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_NOTIFICATION">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_NOTIFICATION" />
+ <usage value="AUDIO_USAGE_NOTIFICATION_EVENT" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_SYSTEM_SOUND">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_EMERGENCY">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_EMERGENCY" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_SAFETY">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_SAFETY" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_VEHICLE_STATUS">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_VEHICLE_STATUS" />
+ </audioAttributes>
+ </oemContext>
+ <oemContext name="OEM_ANNOUNCEMENT">
+ <audioAttributes>
+ <usage value="AUDIO_USAGE_ANNOUNCEMENT" />
+ </audioAttributes>
+ </oemContext>
+ </oemContexts>
+ <zones>
+ <zone isPrimary="true" name="primary zone" audioZoneId="0" occupantZoneId="0">
+ <zoneConfigs>
+ <zoneConfig name="primary zone config 1" isDefault="true">
+ <volumeGroups>
+ <group name="OEM_VOLUME_GROUP">
+ <device address="media_bus_device">
+ <context context="OEM_MUSIC"/>
+ <context context="OEM_NAVIGATION"/>
+ <context context="OEM_VOICE_COMMAND"/>
+ <context context="OEM_CALL_RING"/>
+ <context context="OEM_CALL"/>
+ <context context="OEM_ALARM"/>
+ <context context="OEM_NOTIFICATION"/>
+ <context context="OEM_SYSTEM_SOUND"/>
+ <context context="OEM_EMERGENCY"/>
+ <context context="OEM_SAFETY"/>
+ <context context="OEM_VEHICLE_STATUS"/>
+ <context context="OEM_ANNOUNCEMENT"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zoneConfig>
+ </zoneConfigs>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_unit_test/res/raw/car_built_in_api_classes.txt b/tests/carservice_unit_test/res/raw/car_built_in_api_classes.txt
deleted file mode 100644
index aef0643..0000000
--- a/tests/carservice_unit_test/res/raw/car_built_in_api_classes.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-android.car.builtin.CarBuiltin
-android.car.builtin.PermissionHelper
-android.car.builtin.app.ActivityManagerHelper
-android.car.builtin.app.ActivityManagerHelper$ProcessObserverCallback
-android.car.builtin.app.AppOpsManagerHelper
-android.car.builtin.app.KeyguardManagerHelper
-android.car.builtin.app.TaskInfoHelper
-android.car.builtin.app.VoiceInteractionHelper
-android.car.builtin.bluetooth.BluetoothHeadsetClientHelper
-android.car.builtin.bluetooth.le.AdvertisingSetCallbackHelper
-android.car.builtin.bluetooth.le.AdvertisingSetCallbackHelper$Callback
-android.car.builtin.bluetooth.le.AdvertisingSetHelper
-android.car.builtin.content.ContextHelper
-android.car.builtin.content.pm.PackageManagerHelper
-android.car.builtin.display.DisplayManagerHelper
-android.car.builtin.input.InputManagerHelper
-android.car.builtin.job.JobSchedulerHelper
-android.car.builtin.keyguard.KeyguardServiceDelegate
-android.car.builtin.keyguard.KeyguardServiceDelegate$KeyguardLockedStateCallback
-android.car.builtin.media.AudioManagerHelper
-android.car.builtin.media.AudioManagerHelper$AudioGainInfo
-android.car.builtin.media.AudioManagerHelper$AudioPatchInfo
-android.car.builtin.media.AudioManagerHelper$VolumeAndMuteReceiver
-android.car.builtin.os.BinderHelper
-android.car.builtin.os.BinderHelper$ShellCommandListener
-android.car.builtin.os.BuildHelper
-android.car.builtin.os.ParcelHelper
-android.car.builtin.os.ProcessHelper
-android.car.builtin.os.ServiceManagerHelper
-android.car.builtin.os.SharedMemoryHelper
-android.car.builtin.os.SystemPropertiesHelper
-android.car.builtin.os.TraceHelper
-android.car.builtin.os.UserManagerHelper
-android.car.builtin.power.PowerManagerHelper
-android.car.builtin.provider.SettingsHelper
-android.car.builtin.util.AssistUtilsHelper
-android.car.builtin.util.AssistUtilsHelper$VoiceInteractionSessionShowCallbackHelper
-android.car.builtin.util.AssistUtilsHelper$VoiceInteractionSessionListenerHelper
-android.car.builtin.util.AtomicFileHelper
-android.car.builtin.util.EventLogHelper
-android.car.builtin.util.Slogf
-android.car.builtin.util.TimeUtils
-android.car.builtin.util.TimingsTraceLog
-android.car.builtin.util.UsageStatsManagerHelper
-android.car.builtin.util.ValidationHelper
-android.car.builtin.view.DisplayHelper
-android.car.builtin.view.InputEventHelper
-android.car.builtin.view.KeyEventHelper
-android.car.builtin.view.SurfaceControlHelper
-android.car.builtin.view.TouchableInsetsProvider
-android.car.builtin.view.ViewHelper
-android.car.builtin.widget.LockPatternHelper
-android.car.builtin.window.DisplayAreaOrganizerHelper
-android.car.builtin.window.WindowManagerHelper
diff --git a/tests/carservice_unit_test/res/raw/car_hidden_apis.txt b/tests/carservice_unit_test/res/raw/car_hidden_apis.txt
index a494710..8321999 100644
--- a/tests/carservice_unit_test/res/raw/car_hidden_apis.txt
+++ b/tests/carservice_unit_test/res/raw/car_hidden_apis.txt
@@ -1,356 +1,364 @@
-android.car AoapService int MSG_NEW_DEVICE_ATTACHED
-android.car AoapService int MSG_NEW_DEVICE_ATTACHED_RESPONSE
-android.car AoapService int MSG_CAN_SWITCH_TO_AOAP
-android.car AoapService int MSG_CAN_SWITCH_TO_AOAP_RESPONSE
android.car AoapService String KEY_DEVICE
android.car AoapService String KEY_RESULT
+android.car AoapService int MSG_CAN_SWITCH_TO_AOAP
+android.car AoapService int MSG_CAN_SWITCH_TO_AOAP_RESPONSE
+android.car AoapService int MSG_NEW_DEVICE_ATTACHED
+android.car AoapService int MSG_NEW_DEVICE_ATTACHED_RESPONSE
+android.car ApiVersion String toString()
+android.car ApiVersion T readFromParcel(Parcel source, ApiVersionFactory<T> factory)
android.car ApiVersion boolean equals(Object obj)
android.car ApiVersion int hashCode()
-android.car ApiVersion String toString()
android.car ApiVersion void writeToParcel(Parcel dest)
-android.car ApiVersion T readFromParcel(Parcel source, ApiVersionFactory<T> factory)
-android.car Car String PROPERTY_EMULATED_PLATFORM_VERSION_MAJOR
-android.car Car String PROPERTY_EMULATED_PLATFORM_VERSION_MINOR
-android.car Car String CAR_SERVICE_BINDER_SERVICE_NAME
-android.car Car String EXPERIMENTAL_CAR_USER_SERVICE
-android.car Car String EXPERIMENTAL_CAR_KEYGUARD_SERVICE
-android.car Car String CAR_INSTRUMENT_CLUSTER_SERVICE
-android.car Car String CAR_INTENT_ACTION_RECEIVER_SERVICE
-android.car Car String CLUSTER_HOME_SERVICE
-android.car Car String PERMISSION_CAR_MONITOR_CLUSTER_NAVIGATION_STATE
-android.car Car String PERMISSION_CAR_DISPLAY_IN_CLUSTER
-android.car Car String PERMISSION_BIND_VMS_CLIENT
-android.car Car String PERMISSION_COLLECT_CAR_CPU_INFO
-android.car Car String CAR_TEMPLATE_HOST_RENDERER_SERVICE
-android.car Car String CAR_SERVICE_INTERFACE_NAME
-android.car Car String CAR_CATEGORY_NAVIGATION
-android.car Car ServiceConnection getServiceConnectionListener()
android.car Car Context getContext()
android.car Car Handler getEventHandler()
+android.car Car ServiceConnection getServiceConnectionListener()
+android.car Car String CAR_CATEGORY_NAVIGATION
+android.car Car String CAR_INSTRUMENT_CLUSTER_SERVICE
+android.car Car String CAR_INTENT_ACTION_RECEIVER_SERVICE
+android.car Car String CAR_SERVICE_BINDER_SERVICE_NAME
+android.car Car String CAR_SERVICE_INTERFACE_NAME
+android.car Car String CAR_TEMPLATE_HOST_RENDERER_SERVICE
+android.car Car String CLUSTER_HOME_SERVICE
+android.car Car String EXPERIMENTAL_CAR_KEYGUARD_SERVICE
+android.car Car String EXPERIMENTAL_CAR_USER_SERVICE
+android.car Car String PERMISSION_BIND_VMS_CLIENT
+android.car Car String PERMISSION_CAR_DISPLAY_IN_CLUSTER
+android.car Car String PERMISSION_CAR_MONITOR_CLUSTER_NAVIGATION_STATE
+android.car Car String PERMISSION_COLLECT_CAR_CPU_INFO
+android.car Car String PROPERTY_EMULATED_PLATFORM_VERSION_MAJOR
+android.car Car String PROPERTY_EMULATED_PLATFORM_VERSION_MINOR
android.car Car T handleRemoteExceptionFromCarService(RemoteException e, T returnValue)
-android.car Car void handleRemoteExceptionFromCarService(RemoteException e)
android.car Car T handleRemoteExceptionFromCarService(Service service, RemoteException e, T returnValue)
+android.car Car void handleRemoteExceptionFromCarService(RemoteException e)
android.car Car void handleRemoteExceptionFromCarService(Service service, RemoteException e)
+android.car CarAppFocusManager List<String> getAppTypeOwner(int appType)
android.car CarAppFocusManager int APP_FOCUS_MAX
android.car CarAppFocusManager int[] getActiveAppTypes()
-android.car CarAppFocusManager List<String> getAppTypeOwner(int appType)
android.car CarAppFocusManager void onCarDisconnected()
-android.car CarBugreportManager void requestBugreportForTesting(ParcelFileDescriptor output, ParcelFileDescriptor extraOutput, CarBugreportManagerCallback callback)
android.car CarBugreportManager void onCarDisconnected()
+android.car CarBugreportManager void requestBugreportForTesting(ParcelFileDescriptor output, ParcelFileDescriptor extraOutput, CarBugreportManagerCallback callback)
android.car CarFeatures String FEATURE_CAR_USER_NOTICE_SERVICE
-android.car CarInfoManager int BASIC_INFO_KEY_MANUFACTURER
-android.car CarInfoManager int BASIC_INFO_KEY_MODEL
-android.car CarInfoManager int BASIC_INFO_KEY_MODEL_YEAR
android.car CarInfoManager String BASIC_INFO_KEY_VEHICLE_ID
android.car CarInfoManager String INFO_KEY_PRODUCT_CONFIGURATION
android.car CarInfoManager int BASIC_INFO_DRIVER_SEAT
-android.car CarInfoManager int BASIC_INFO_EV_PORT_LOCATION
-android.car CarInfoManager int BASIC_INFO_FUEL_DOOR_LOCATION
-android.car CarInfoManager int BASIC_INFO_FUEL_CAPACITY
-android.car CarInfoManager int BASIC_INFO_FUEL_TYPES
android.car CarInfoManager int BASIC_INFO_EV_BATTERY_CAPACITY
android.car CarInfoManager int BASIC_INFO_EV_CONNECTOR_TYPES
+android.car CarInfoManager int BASIC_INFO_EV_PORT_LOCATION
+android.car CarInfoManager int BASIC_INFO_FUEL_CAPACITY
+android.car CarInfoManager int BASIC_INFO_FUEL_DOOR_LOCATION
+android.car CarInfoManager int BASIC_INFO_FUEL_TYPES
+android.car CarInfoManager int BASIC_INFO_KEY_MANUFACTURER
+android.car CarInfoManager int BASIC_INFO_KEY_MODEL
+android.car CarInfoManager int BASIC_INFO_KEY_MODEL_YEAR
android.car CarInfoManager void onCarDisconnected()
android.car CarLibLog String TAG_CAR
android.car CarLibLog String TAG_CLUSTER
+android.car CarLibLog String TAG_DIAGNOSTIC
android.car CarLibLog String TAG_INPUT
android.car CarLibLog String TAG_NAV
android.car CarLibLog String TAG_SENSOR
-android.car CarLibLog String TAG_DIAGNOSTIC
android.car CarManagerBase Car mCar
android.car CarManagerBase Context getContext()
android.car CarManagerBase Handler getEventHandler()
+android.car CarManagerBase T addDumpable(Object container, Supplier<T> dumpableSupplier)
+android.car CarManagerBase T handleExceptionFromCarService(Exception e, T returnValue)
android.car CarManagerBase T handleRemoteExceptionFromCarService(RemoteException e, T returnValue)
android.car CarManagerBase void handleRemoteExceptionFromCarService(RemoteException e)
-android.car CarManagerBase T handleExceptionFromCarService(Exception e, T returnValue)
android.car CarManagerBase void onCarDisconnected()
-android.car CarManagerBase T addDumpable(Object container, Supplier<T> dumpableSupplier)
-android.car CarOccupantZoneManager int OCCUPANT_TYPE_INVALID
android.car CarOccupantZoneManager boolean assignProfileUserToOccupantZone(OccupantZoneInfo occupantZone, int userId)
+android.car CarOccupantZoneManager int OCCUPANT_TYPE_INVALID
android.car CarOccupantZoneManager void onCarDisconnected()
android.car CarOccupantZoneManager.OccupantZoneInfo int INVALID_ZONE_ID
android.car CarProjectionManager int NUM_KEY_EVENTS
+android.car CarProjectionManager int PROJECTION_AP_FAILED
android.car CarProjectionManager int PROJECTION_AP_STARTED
android.car CarProjectionManager int PROJECTION_AP_STOPPED
-android.car CarProjectionManager int PROJECTION_AP_FAILED
+android.car CarProjectionManager void onCarDisconnected()
android.car CarProjectionManager void regsiterProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
android.car CarProjectionManager void unregsiterProjectionListener()
-android.car CarProjectionManager void onCarDisconnected()
android.car CarRemoteDeviceManager void onCarDisconnected()
-android.car VehicleAreaSeat int SIDE_LEFT
android.car VehicleAreaSeat int SIDE_CENTER
+android.car VehicleAreaSeat int SIDE_LEFT
android.car VehicleAreaSeat int SIDE_RIGHT
android.car VehicleAreaSeat int fromRowAndSide(int rowNumber, int side)
+android.car VehicleHvacFanDirection int DEFROST
android.car VehicleHvacFanDirection int FACE
android.car VehicleHvacFanDirection int FLOOR
-android.car VehicleHvacFanDirection int DEFROST
+android.car VehicleLightState int DAYTIME_RUNNING
android.car VehicleLightState int OFF
android.car VehicleLightState int ON
-android.car VehicleLightState int DAYTIME_RUNNING
+android.car VehicleLightSwitch int AUTOMATIC
+android.car VehicleLightSwitch int DAYTIME_RUNNING
android.car VehicleLightSwitch int OFF
android.car VehicleLightSwitch int ON
-android.car VehicleLightSwitch int DAYTIME_RUNNING
-android.car VehicleLightSwitch int AUTOMATIC
android.car VehicleOilLevel int CRITICALLY_LOW
+android.car VehicleOilLevel int ERROR
+android.car VehicleOilLevel int HIGH
android.car VehicleOilLevel int LOW
android.car VehicleOilLevel int NORMAL
-android.car VehicleOilLevel int HIGH
-android.car VehicleOilLevel int ERROR
-android.car VehiclePropertyIds int INITIAL_USER_INFO
-android.car VehiclePropertyIds int SWITCH_USER
-android.car VehiclePropertyIds int CREATE_USER
-android.car VehiclePropertyIds int REMOVE_USER
-android.car VehiclePropertyIds int USER_IDENTIFICATION_ASSOCIATION
-android.car VehiclePropertyIds int POWER_POLICY_REQ
-android.car VehiclePropertyIds int POWER_POLICY_GROUP_REQ
-android.car VehiclePropertyIds int CURRENT_POWER_POLICY
-android.car VehiclePropertyIds int WATCHDOG_ALIVE
-android.car VehiclePropertyIds int WATCHDOG_TERMINATED_PROCESS
-android.car VehiclePropertyIds int VHAL_HEARTBEAT
-android.car VehiclePropertyIds int CLUSTER_SWITCH_UI
android.car VehiclePropertyIds int CLUSTER_DISPLAY_STATE
+android.car VehiclePropertyIds int CLUSTER_NAVIGATION_STATE
android.car VehiclePropertyIds int CLUSTER_REPORT_STATE
android.car VehiclePropertyIds int CLUSTER_REQUEST_DISPLAY
-android.car VehiclePropertyIds int CLUSTER_NAVIGATION_STATE
-android.car VehiclePropertyType int STRING
+android.car VehiclePropertyIds int CLUSTER_SWITCH_UI
+android.car VehiclePropertyIds int CREATE_USER
+android.car VehiclePropertyIds int CURRENT_POWER_POLICY
+android.car VehiclePropertyIds int INITIAL_USER_INFO
+android.car VehiclePropertyIds int POWER_POLICY_GROUP_REQ
+android.car VehiclePropertyIds int POWER_POLICY_REQ
+android.car VehiclePropertyIds int REMOVE_USER
+android.car VehiclePropertyIds int SWITCH_USER
+android.car VehiclePropertyIds int USER_IDENTIFICATION_ASSOCIATION
+android.car VehiclePropertyIds int VHAL_HEARTBEAT
+android.car VehiclePropertyIds int WATCHDOG_ALIVE
+android.car VehiclePropertyIds int WATCHDOG_TERMINATED_PROCESS
android.car VehiclePropertyType int BOOLEAN
+android.car VehiclePropertyType int BYTES
+android.car VehiclePropertyType int FLOAT
+android.car VehiclePropertyType int FLOAT_VEC
android.car VehiclePropertyType int INT32
android.car VehiclePropertyType int INT32_VEC
android.car VehiclePropertyType int INT64
android.car VehiclePropertyType int INT64_VEC
-android.car VehiclePropertyType int FLOAT
-android.car VehiclePropertyType int FLOAT_VEC
-android.car VehiclePropertyType int BYTES
-android.car VehiclePropertyType int MIXED
android.car VehiclePropertyType int MASK
+android.car VehiclePropertyType int MIXED
+android.car VehiclePropertyType int STRING
+android.car VehicleSeatOccupancyState int OCCUPIED
android.car VehicleSeatOccupancyState int UNKNOWN
android.car VehicleSeatOccupancyState int VACANT
-android.car VehicleSeatOccupancyState int OCCUPIED
+android.car.admin CarDevicePolicyManager StartUserInBackgroundResult startUserInBackground(UserHandle user)
+android.car.admin CarDevicePolicyManager StopUserResult stopUser(UserHandle user)
android.car.admin CarDevicePolicyManager String TAG
android.car.admin CarDevicePolicyManager int FIRST_USER_TYPE
android.car.admin CarDevicePolicyManager int LAST_USER_TYPE
-android.car.admin CarDevicePolicyManager StartUserInBackgroundResult startUserInBackground(UserHandle user)
-android.car.admin CarDevicePolicyManager StopUserResult stopUser(UserHandle user)
-android.car.admin CarDevicePolicyManager void setUserDisclaimerShown(UserHandle user)
-android.car.admin CarDevicePolicyManager void setUserDisclaimerAcknowledged(UserHandle user)
android.car.admin CarDevicePolicyManager void onCarDisconnected()
+android.car.admin CarDevicePolicyManager void setUserDisclaimerAcknowledged(UserHandle user)
+android.car.admin CarDevicePolicyManager void setUserDisclaimerShown(UserHandle user)
android.car.admin CreateUserResult CreateUserResult forGenericError()
android.car.admin RemoveUserResult String statusToString(int status)
+android.car.admin StartUserInBackgroundResult String toString()
+android.car.admin StartUserInBackgroundResult boolean isSuccess()
+android.car.admin StartUserInBackgroundResult int STATUS_FAILURE_GENERIC
+android.car.admin StartUserInBackgroundResult int STATUS_FAILURE_USER_DOES_NOT_EXIST
android.car.admin StartUserInBackgroundResult int STATUS_SUCCESS
android.car.admin StartUserInBackgroundResult int STATUS_SUCCESS_CURRENT_USER
-android.car.admin StartUserInBackgroundResult int STATUS_FAILURE_USER_DOES_NOT_EXIST
-android.car.admin StartUserInBackgroundResult int STATUS_FAILURE_GENERIC
android.car.admin StartUserInBackgroundResult int getStatus()
-android.car.admin StartUserInBackgroundResult boolean isSuccess()
-android.car.admin StartUserInBackgroundResult String toString()
-android.car.admin StopUserResult int STATUS_SUCCESS
+android.car.admin StopUserResult String toString()
+android.car.admin StopUserResult boolean isSuccess()
android.car.admin StopUserResult int STATUS_FAILURE_CURRENT_USER
+android.car.admin StopUserResult int STATUS_FAILURE_GENERIC
android.car.admin StopUserResult int STATUS_FAILURE_SYSTEM_USER
android.car.admin StopUserResult int STATUS_FAILURE_USER_DOES_NOT_EXIST
-android.car.admin StopUserResult int STATUS_FAILURE_GENERIC
+android.car.admin StopUserResult int STATUS_SUCCESS
android.car.admin StopUserResult int getStatus()
-android.car.admin StopUserResult boolean isSuccess()
-android.car.admin StopUserResult String toString()
+android.car.app CarActivityManager Pair<SurfaceControl,Rect> getMirroredSurface(IBinder token)
+android.car.app CarActivityManager boolean registerTaskMonitor()
android.car.app CarActivityManager int ERROR_CODE_ACTIVITY_NOT_FOUND
android.car.app CarActivityManager void onCarDisconnected()
-android.car.app CarActivityManager boolean registerTaskMonitor()
android.car.app CarActivityManager void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo)
android.car.app CarActivityManager void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)
-android.car.app CarActivityManager void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)
android.car.app CarActivityManager void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)
+android.car.app CarActivityManager void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)
android.car.app CarActivityManager void unregisterTaskMonitor()
-android.car.app CarActivityManager Pair<SurfaceControl,Rect> getMirroredSurface(IBinder token)
+android.car.app CarTaskViewControllerHostLifecycle boolean isVisible()
+android.car.app CarTaskViewControllerHostLifecycle void hostAppeared()
+android.car.app CarTaskViewControllerHostLifecycle void hostDestroyed()
+android.car.app CarTaskViewControllerHostLifecycle void hostDisappeared()
+android.car.app CarTaskViewControllerHostLifecycle void registerObserver(CarTaskViewControllerHostLifecycleObserver observer)
+android.car.app CarTaskViewControllerHostLifecycle void unregisterObserver(CarTaskViewControllerHostLifecycleObserver observer)
+android.car.app CarTaskViewControllerHostLifecycleFactory CarTaskViewControllerHostLifecycle forActivity(Activity activity)
android.car.cluster CarInstrumentClusterManager void onCarDisconnected()
-android.car.cluster ClusterHomeManager int UI_TYPE_CLUSTER_NONE
-android.car.cluster ClusterHomeManager int UI_TYPE_CLUSTER_HOME
-android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_ON_OFF
-android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_BOUNDS
-android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_INSETS
-android.car.cluster ClusterHomeManager int CONFIG_UI_TYPE
-android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_ID
-android.car.cluster ClusterHomeManager void registerClusterStateListener(Executor executor, ClusterStateListener callback)
-android.car.cluster ClusterHomeManager void registerClusterNavigationStateListener(Executor executor, ClusterNavigationStateListener callback)
-android.car.cluster ClusterHomeManager void unregisterClusterStateListener(ClusterStateListener callback)
-android.car.cluster ClusterHomeManager void unregisterClusterNavigationStateListener(ClusterNavigationStateListener callback)
-android.car.cluster ClusterHomeManager void reportState(int uiTypeMain, int uiTypeSub, byte[] uiAvailability)
-android.car.cluster ClusterHomeManager void requestDisplay(int uiType)
android.car.cluster ClusterHomeManager ClusterState getClusterState()
android.car.cluster ClusterHomeManager boolean startFixedActivityModeAsUser(Intent intent, Bundle options, int userId)
-android.car.cluster ClusterHomeManager void stopFixedActivityMode()
+android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_BOUNDS
+android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_ID
+android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_INSETS
+android.car.cluster ClusterHomeManager int CONFIG_DISPLAY_ON_OFF
+android.car.cluster ClusterHomeManager int CONFIG_UI_TYPE
+android.car.cluster ClusterHomeManager int UI_TYPE_CLUSTER_HOME
+android.car.cluster ClusterHomeManager int UI_TYPE_CLUSTER_NONE
android.car.cluster ClusterHomeManager void onCarDisconnected()
-android.car.cluster.renderer InstrumentClusterRenderingService String EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER
+android.car.cluster ClusterHomeManager void registerClusterNavigationStateListener(Executor executor, ClusterNavigationStateListener callback)
+android.car.cluster ClusterHomeManager void registerClusterStateListener(Executor executor, ClusterStateListener callback)
+android.car.cluster ClusterHomeManager void reportState(int uiTypeMain, int uiTypeSub, byte[] uiAvailability)
+android.car.cluster ClusterHomeManager void requestDisplay(int uiType)
+android.car.cluster ClusterHomeManager void stopFixedActivityMode()
+android.car.cluster ClusterHomeManager void unregisterClusterNavigationStateListener(ClusterNavigationStateListener callback)
+android.car.cluster ClusterHomeManager void unregisterClusterStateListener(ClusterStateListener callback)
android.car.cluster.renderer InstrumentClusterRenderingService ComponentName getComponentFromPackage(String packageName)
+android.car.cluster.renderer InstrumentClusterRenderingService String EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER
android.car.cluster.renderer InstrumentClusterRenderingService void setClusterActivityLaunchOptions(String category, ActivityOptions activityOptions)
android.car.cluster.renderer InstrumentClusterRenderingService void setClusterActivityState(String category, Bundle state)
-android.car.content.pm AppBlockingPackageInfo void verify()
android.car.content.pm AppBlockingPackageInfo boolean isActivityCovered(String className)
+android.car.content.pm AppBlockingPackageInfo void verify()
+android.car.content.pm CarPackageManager List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName, String activityClassName, int userId)
android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME
android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID
-android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME
-android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO
android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_DISPLAY_ID
-android.car.content.pm CarPackageManager String DRIVING_SAFETY_REGION_ALL
+android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO
+android.car.content.pm CarPackageManager String BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME
android.car.content.pm CarPackageManager String DRIVING_SAFETY_ACTIVITY_METADATA_REGIONS
+android.car.content.pm CarPackageManager String DRIVING_SAFETY_REGION_ALL
+android.car.content.pm CarPackageManager String getCurrentDrivingSafetyRegion()
android.car.content.pm CarPackageManager int ERROR_CODE_NO_PACKAGE
+android.car.content.pm CarPackageManager void controlTemporaryActivityBlockingBypassingAsUser(String packageName, String activityClassName, boolean bypass, int userId)
android.car.content.pm CarPackageManager void onCarDisconnected()
android.car.content.pm CarPackageManager void restartTask(int taskId)
android.car.content.pm CarPackageManager void setEnableActivityBlocking(boolean enable)
-android.car.content.pm CarPackageManager String getCurrentDrivingSafetyRegion()
-android.car.content.pm CarPackageManager void controlTemporaryActivityBlockingBypassingAsUser(String packageName, String activityClassName, boolean bypass, int userId)
-android.car.content.pm CarPackageManager List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName, String activityClassName, int userId)
-android.car.diagnostic CarDiagnosticEvent CarDiagnosticEvent withVendorSensorsRemoved()
-android.car.diagnostic CarDiagnosticEvent boolean isEmptyFrame()
-android.car.diagnostic CarDiagnosticEvent CarDiagnosticEvent checkLiveFrame()
android.car.diagnostic CarDiagnosticEvent CarDiagnosticEvent checkFreezeFrame()
+android.car.diagnostic CarDiagnosticEvent CarDiagnosticEvent checkLiveFrame()
+android.car.diagnostic CarDiagnosticEvent CarDiagnosticEvent withVendorSensorsRemoved()
android.car.diagnostic CarDiagnosticEvent boolean isEarlierThan(CarDiagnosticEvent otherEvent)
-android.car.diagnostic CarDiagnosticEvent.IgnitionMonitor.Decoder IgnitionMonitor fromValue(int value)
+android.car.diagnostic CarDiagnosticEvent boolean isEmptyFrame()
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int COMPONENTS_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int COMPONENTS_INCOMPLETE
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int FUEL_SYSTEM_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int FUEL_SYSTEM_INCOMPLETE
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int MISFIRE_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.CommonIgnitionMonitors int MISFIRE_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EGR_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EGR_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_HEATER_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_HEATER_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int BOOST_PRESSURE_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int BOOST_PRESSURE_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EGR_OR_VVT_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EGR_OR_VVT_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EXHAUST_GAS_SENSOR_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EXHAUST_GAS_SENSOR_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NMHC_CATALYST_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NMHC_CATALYST_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NOx_SCR_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NOx_SCR_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int PM_FILTER_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int PM_FILTER_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.IgnitionMonitor.Decoder IgnitionMonitor fromValue(int value)
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int AC_REFRIGERANT_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int AC_REFRIGERANT_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int SECONDARY_AIR_SYSTEM_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int SECONDARY_AIR_SYSTEM_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int CATALYST_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int CATALYST_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EGR_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EGR_INCOMPLETE
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EVAPORATIVE_SYSTEM_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int EVAPORATIVE_SYSTEM_INCOMPLETE
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int HEATED_CATALYST_AVAILABLE
android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int HEATED_CATALYST_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int CATALYST_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int CATALYST_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EGR_OR_VVT_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EGR_OR_VVT_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int PM_FILTER_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int PM_FILTER_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EXHAUST_GAS_SENSOR_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int EXHAUST_GAS_SENSOR_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int BOOST_PRESSURE_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int BOOST_PRESSURE_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NOx_SCR_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NOx_SCR_INCOMPLETE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NMHC_CATALYST_AVAILABLE
-android.car.diagnostic CarDiagnosticEvent.CompressionIgnitionMonitors int NMHC_CATALYST_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_HEATER_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_HEATER_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int OXYGEN_SENSOR_INCOMPLETE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int SECONDARY_AIR_SYSTEM_AVAILABLE
+android.car.diagnostic CarDiagnosticEvent.SparkIgnitionMonitors int SECONDARY_AIR_SYSTEM_INCOMPLETE
android.car.diagnostic CarDiagnosticManager int[] FRAME_TYPES
-android.car.drivingstate CarDrivingStateManager void onCarDisconnected()
android.car.drivingstate CarDrivingStateManager void injectDrivingState(int drivingState)
+android.car.drivingstate CarDrivingStateManager void onCarDisconnected()
android.car.drivingstate CarUxRestrictions long getTimeStamp()
-android.car.drivingstate CarUxRestrictionsConfiguration void writeJson(JsonWriter writer)
android.car.drivingstate CarUxRestrictionsConfiguration CarUxRestrictionsConfiguration readJson(JsonReader reader, int schemaVersion)
android.car.drivingstate CarUxRestrictionsConfiguration boolean hasSameParameters(CarUxRestrictionsConfiguration other)
android.car.drivingstate CarUxRestrictionsConfiguration void dump(PrintWriter writer)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Map<String,RestrictionModeContainer> mRestrictionModes
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validatePort(int port)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validateOccupantZoneId(int zoneId)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validateDisplayType(int displayType)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setPhysicalPort(int port)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setOccupantZoneId(int occupantZoneId)
+android.car.drivingstate CarUxRestrictionsConfiguration void writeJson(JsonWriter writer)
android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setDisplayType(int displayType)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, boolean requiresOptimization, int restrictions)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, SpeedRange speedRange, boolean requiresOptimization, int restrictions)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, DrivingStateRestrictions drivingStateRestrictions)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setMaxStringLength(int maxStringLength)
-android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setMaxCumulativeContentItems(int maxCumulativeContentItems)
android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setMaxContentDepth(int maxContentDepth)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setMaxCumulativeContentItems(int maxCumulativeContentItems)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setMaxStringLength(int maxStringLength)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setOccupantZoneId(int occupantZoneId)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setPhysicalPort(int port)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, DrivingStateRestrictions drivingStateRestrictions)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, SpeedRange speedRange, boolean requiresOptimization, int restrictions)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Builder setUxRestrictions(int drivingState, boolean requiresOptimization, int restrictions)
android.car.drivingstate CarUxRestrictionsConfiguration.Builder CarUxRestrictionsConfiguration build()
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder Map<String,RestrictionModeContainer> mRestrictionModes
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validateDisplayType(int displayType)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validateOccupantZoneId(int zoneId)
+android.car.drivingstate CarUxRestrictionsConfiguration.Builder int validatePort(int port)
android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions DrivingStateRestrictions setDistractionOptimizationRequired(boolean distractionOptimizationRequired)
-android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions DrivingStateRestrictions setRestrictions(int restrictions)
android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions DrivingStateRestrictions setMode(String mode)
+android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions DrivingStateRestrictions setRestrictions(int restrictions)
android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions DrivingStateRestrictions setSpeedRange(Builder.SpeedRange speedRange)
android.car.drivingstate CarUxRestrictionsConfiguration.DrivingStateRestrictions String toString()
android.car.drivingstate CarUxRestrictionsManager void registerListener(OnUxRestrictionsChangedListener listener, int displayId)
android.car.evs CarEvsManager void onCarDisconnected()
+android.car.hardware CarPropertyConfig Builder<T> newBuilder(Class<T> type, int propertyId, int areaType)
android.car.hardware CarPropertyConfig String getConfigString()
+android.car.hardware CarPropertyConfig String toString()
+android.car.hardware CarPropertyConfig boolean hasArea(int areaId)
android.car.hardware CarPropertyConfig int getAreaCount()
android.car.hardware CarPropertyConfig int getFirstAndOnlyAreaId()
-android.car.hardware CarPropertyConfig boolean hasArea(int areaId)
-android.car.hardware CarPropertyConfig String toString()
-android.car.hardware CarPropertyConfig Builder<T> newBuilder(Class<T> type, int propertyId, int areaType)
android.car.hardware CarPropertyConfig.AreaConfig Parcelable.Creator<AreaConfig<Object>> CREATOR
-android.car.hardware CarPropertyConfig.AreaConfig T getMinValue()
+android.car.hardware CarPropertyConfig.AreaConfig String toString()
android.car.hardware CarPropertyConfig.AreaConfig T getMaxValue()
+android.car.hardware CarPropertyConfig.AreaConfig T getMinValue()
android.car.hardware CarPropertyConfig.AreaConfig int describeContents()
android.car.hardware CarPropertyConfig.AreaConfig void writeToParcel(Parcel dest, int flags)
-android.car.hardware CarPropertyConfig.AreaConfig String toString()
android.car.hardware CarPropertyValue String toString()
-android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS
+android.car.hardware CarSensorConfig Bundle getBundle()
+android.car.hardware CarSensorConfig Parcelable.Creator<CarSensorConfig> CREATOR
android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK
android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK
-android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK
android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK
-android.car.hardware CarSensorConfig Parcelable.Creator<CarSensorConfig> CREATOR
+android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK
+android.car.hardware CarSensorConfig String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS
+android.car.hardware CarSensorConfig String toString()
android.car.hardware CarSensorConfig int describeContents()
-android.car.hardware CarSensorConfig void writeToParcel(Parcel dest, int flags)
-android.car.hardware CarSensorConfig Bundle getBundle()
android.car.hardware CarSensorConfig int getInt(String key)
android.car.hardware CarSensorConfig int getType()
-android.car.hardware CarSensorConfig String toString()
+android.car.hardware CarSensorConfig void writeToParcel(Parcel dest, int flags)
+android.car.hardware CarSensorEvent CarAbsActiveData getCarAbsActiveData(CarAbsActiveData dataParam)
+android.car.hardware CarSensorEvent CarEngineOilLevelData getCarEngineOilLevelData(CarEngineOilLevelData dataParam)
+android.car.hardware CarSensorEvent CarEvBatteryChargeRateData getCarEvBatteryChargeRateData(CarEvBatteryChargeRateData dataParam)
+android.car.hardware CarSensorEvent CarEvBatteryLevelData getCarEvBatteryLevelData(CarEvBatteryLevelData dataParam)
+android.car.hardware CarSensorEvent CarEvChargePortConnectedData getCarEvChargePortConnectedData(CarEvChargePortConnectedData dataParam)
+android.car.hardware CarSensorEvent CarEvChargePortOpenData getCarEvChargePortOpenData(CarEvChargePortOpenData dataParam)
+android.car.hardware CarSensorEvent CarFuelDoorOpenData getCarFuelDoorOpenData(CarFuelDoorOpenData dataParam)
+android.car.hardware CarSensorEvent CarSpeedData getCarSpeedData(CarSpeedData dataParam)
+android.car.hardware CarSensorEvent CarTractionControlActiveData getCarTractionControlActiveData(CarTractionControlActiveData dataParam)
+android.car.hardware CarSensorEvent CarWheelTickDistanceData getCarWheelTickDistanceData(CarWheelTickDistanceData dataParam)
android.car.hardware CarSensorEvent EnvironmentData getEnvironmentData(EnvironmentData outData)
+android.car.hardware CarSensorEvent FuelLevelData getFuelLevelData(FuelLevelData dataParam)
+android.car.hardware CarSensorEvent GearData getGearData(GearData dataParam)
android.car.hardware CarSensorEvent IgnitionStateData getIgnitionStateData(IgnitionStateData dataParam)
android.car.hardware CarSensorEvent NightData getNightData(NightData dataParam)
-android.car.hardware CarSensorEvent GearData getGearData(GearData dataParam)
-android.car.hardware CarSensorEvent ParkingBrakeData getParkingBrakeData(ParkingBrakeData dataParam)
-android.car.hardware CarSensorEvent FuelLevelData getFuelLevelData(FuelLevelData dataParam)
android.car.hardware CarSensorEvent OdometerData getOdometerData(OdometerData dataParam)
+android.car.hardware CarSensorEvent ParkingBrakeData getParkingBrakeData(ParkingBrakeData dataParam)
android.car.hardware CarSensorEvent RpmData getRpmData(RpmData dataParam)
-android.car.hardware CarSensorEvent CarSpeedData getCarSpeedData(CarSpeedData dataParam)
-android.car.hardware CarSensorEvent CarWheelTickDistanceData getCarWheelTickDistanceData(CarWheelTickDistanceData dataParam)
-android.car.hardware CarSensorEvent CarAbsActiveData getCarAbsActiveData(CarAbsActiveData dataParam)
-android.car.hardware CarSensorEvent CarTractionControlActiveData getCarTractionControlActiveData(CarTractionControlActiveData dataParam)
-android.car.hardware CarSensorEvent CarFuelDoorOpenData getCarFuelDoorOpenData(CarFuelDoorOpenData dataParam)
-android.car.hardware CarSensorEvent CarEvBatteryLevelData getCarEvBatteryLevelData(CarEvBatteryLevelData dataParam)
-android.car.hardware CarSensorEvent CarEvChargePortOpenData getCarEvChargePortOpenData(CarEvChargePortOpenData dataParam)
-android.car.hardware CarSensorEvent CarEvChargePortConnectedData getCarEvChargePortConnectedData(CarEvChargePortConnectedData dataParam)
-android.car.hardware CarSensorEvent CarEvBatteryChargeRateData getCarEvBatteryChargeRateData(CarEvBatteryChargeRateData dataParam)
-android.car.hardware CarSensorEvent CarEngineOilLevelData getCarEngineOilLevelData(CarEngineOilLevelData dataParam)
android.car.hardware CarSensorEvent String toString()
-android.car.hardware CarSensorEvent.IgnitionStateData long timestamp
-android.car.hardware CarSensorEvent.IgnitionStateData int ignitionState
-android.car.hardware CarSensorEvent.NightData long timestamp
-android.car.hardware CarSensorEvent.NightData boolean isNightMode
-android.car.hardware CarSensorEvent.GearData long timestamp
-android.car.hardware CarSensorEvent.GearData int gear
-android.car.hardware CarSensorEvent.ParkingBrakeData long timestamp
-android.car.hardware CarSensorEvent.ParkingBrakeData boolean isEngaged
-android.car.hardware CarSensorEvent.FuelLevelData long timestamp
-android.car.hardware CarSensorEvent.FuelLevelData float level
-android.car.hardware CarSensorEvent.OdometerData long timestamp
-android.car.hardware CarSensorEvent.OdometerData float kms
-android.car.hardware CarSensorEvent.RpmData long timestamp
-android.car.hardware CarSensorEvent.RpmData float rpm
-android.car.hardware CarSensorEvent.CarSpeedData long timestamp
+android.car.hardware CarSensorEvent.CarAbsActiveData boolean absIsActive
+android.car.hardware CarSensorEvent.CarAbsActiveData long timestamp
+android.car.hardware CarSensorEvent.CarEngineOilLevelData int engineOilLevel
+android.car.hardware CarSensorEvent.CarEngineOilLevelData long timestamp
+android.car.hardware CarSensorEvent.CarEvBatteryChargeRateData float evChargeRate
+android.car.hardware CarSensorEvent.CarEvBatteryChargeRateData long timestamp
+android.car.hardware CarSensorEvent.CarEvBatteryLevelData float evBatteryLevel
+android.car.hardware CarSensorEvent.CarEvBatteryLevelData long timestamp
+android.car.hardware CarSensorEvent.CarEvChargePortConnectedData boolean evChargePortIsConnected
+android.car.hardware CarSensorEvent.CarEvChargePortConnectedData long timestamp
+android.car.hardware CarSensorEvent.CarEvChargePortOpenData boolean evChargePortIsOpen
+android.car.hardware CarSensorEvent.CarEvChargePortOpenData long timestamp
+android.car.hardware CarSensorEvent.CarFuelDoorOpenData boolean fuelDoorIsOpen
+android.car.hardware CarSensorEvent.CarFuelDoorOpenData long timestamp
android.car.hardware CarSensorEvent.CarSpeedData float carSpeed
-android.car.hardware CarSensorEvent.CarWheelTickDistanceData long timestamp
-android.car.hardware CarSensorEvent.CarWheelTickDistanceData long sensorResetCount
+android.car.hardware CarSensorEvent.CarSpeedData long timestamp
+android.car.hardware CarSensorEvent.CarTractionControlActiveData boolean tractionControlIsActive
+android.car.hardware CarSensorEvent.CarTractionControlActiveData long timestamp
android.car.hardware CarSensorEvent.CarWheelTickDistanceData long frontLeftWheelDistanceMm
android.car.hardware CarSensorEvent.CarWheelTickDistanceData long frontRightWheelDistanceMm
-android.car.hardware CarSensorEvent.CarWheelTickDistanceData long rearRightWheelDistanceMm
android.car.hardware CarSensorEvent.CarWheelTickDistanceData long rearLeftWheelDistanceMm
-android.car.hardware CarSensorEvent.CarAbsActiveData long timestamp
-android.car.hardware CarSensorEvent.CarAbsActiveData boolean absIsActive
-android.car.hardware CarSensorEvent.CarTractionControlActiveData long timestamp
-android.car.hardware CarSensorEvent.CarTractionControlActiveData boolean tractionControlIsActive
-android.car.hardware CarSensorEvent.CarFuelDoorOpenData long timestamp
-android.car.hardware CarSensorEvent.CarFuelDoorOpenData boolean fuelDoorIsOpen
-android.car.hardware CarSensorEvent.CarEvBatteryLevelData long timestamp
-android.car.hardware CarSensorEvent.CarEvBatteryLevelData float evBatteryLevel
-android.car.hardware CarSensorEvent.CarEvChargePortOpenData long timestamp
-android.car.hardware CarSensorEvent.CarEvChargePortOpenData boolean evChargePortIsOpen
-android.car.hardware CarSensorEvent.CarEvChargePortConnectedData long timestamp
-android.car.hardware CarSensorEvent.CarEvChargePortConnectedData boolean evChargePortIsConnected
-android.car.hardware CarSensorEvent.CarEvBatteryChargeRateData long timestamp
-android.car.hardware CarSensorEvent.CarEvBatteryChargeRateData float evChargeRate
-android.car.hardware CarSensorEvent.CarEngineOilLevelData long timestamp
-android.car.hardware CarSensorEvent.CarEngineOilLevelData int engineOilLevel
+android.car.hardware CarSensorEvent.CarWheelTickDistanceData long rearRightWheelDistanceMm
+android.car.hardware CarSensorEvent.CarWheelTickDistanceData long sensorResetCount
+android.car.hardware CarSensorEvent.CarWheelTickDistanceData long timestamp
+android.car.hardware CarSensorEvent.FuelLevelData float level
+android.car.hardware CarSensorEvent.FuelLevelData long timestamp
+android.car.hardware CarSensorEvent.GearData int gear
+android.car.hardware CarSensorEvent.GearData long timestamp
+android.car.hardware CarSensorEvent.IgnitionStateData int ignitionState
+android.car.hardware CarSensorEvent.IgnitionStateData long timestamp
+android.car.hardware CarSensorEvent.NightData boolean isNightMode
+android.car.hardware CarSensorEvent.NightData long timestamp
+android.car.hardware CarSensorEvent.OdometerData float kms
+android.car.hardware CarSensorEvent.OdometerData long timestamp
+android.car.hardware CarSensorEvent.ParkingBrakeData boolean isEngaged
+android.car.hardware CarSensorEvent.ParkingBrakeData long timestamp
+android.car.hardware CarSensorEvent.RpmData float rpm
+android.car.hardware CarSensorEvent.RpmData long timestamp
+android.car.hardware CarSensorManager CarSensorConfig getSensorConfig(int type)
+android.car.hardware CarSensorManager boolean isSensorSupported(int[] sensorList, int sensorType)
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED1
-android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED8
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED10
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED11
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED12
@@ -364,165 +372,169 @@
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED20
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED21
android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED26
+android.car.hardware CarSensorManager int SENSOR_TYPE_RESERVED8
android.car.hardware CarSensorManager void onCarDisconnected()
-android.car.hardware CarSensorManager boolean isSensorSupported(int[] sensorList, int sensorType)
-android.car.hardware CarSensorManager CarSensorConfig getSensorConfig(int type)
android.car.hardware CarVendorExtensionManager void onCarDisconnected()
android.car.hardware.cabin CarCabinManager void onCarDisconnected()
android.car.hardware.hvac CarHvacManager void onCarDisconnected()
android.car.hardware.power CarPowerManager String TAG
+android.car.hardware.power CarPowerManager boolean isCompletionAllowed(int state)
+android.car.hardware.power CarPowerManager void notifyUserActivity(int displayId)
+android.car.hardware.power CarPowerManager void onCarDisconnected()
android.car.hardware.power CarPowerManager void requestShutdownOnNextSuspend()
android.car.hardware.power CarPowerManager void scheduleNextWakeupTime(int seconds)
-android.car.hardware.power CarPowerManager void notifyUserActivity(int displayId)
-android.car.hardware.power CarPowerManager boolean isCompletionAllowed(int state)
-android.car.hardware.power CarPowerManager void onCarDisconnected()
-android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_ENABLED
-android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_DISABLED
-android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_UNTOUCHED
-android.car.hardware.power PowerComponentUtil int INVALID_POWER_COMPONENT
-android.car.hardware.power PowerComponentUtil int FIRST_POWER_COMPONENT
-android.car.hardware.power PowerComponentUtil int LAST_POWER_COMPONENT
-android.car.hardware.power PowerComponentUtil boolean isValidPowerComponent(int component)
-android.car.hardware.power PowerComponentUtil boolean hasComponents(CarPowerPolicy policy, CarPowerPolicyFilter filter)
-android.car.hardware.power PowerComponentUtil int toPowerComponent(String componentArg, boolean prefix)
android.car.hardware.power PowerComponentUtil String powerComponentToString(int component)
-android.car.hardware.property CarPropertyEvent int PROPERTY_EVENT_PROPERTY_CHANGE
-android.car.hardware.property CarPropertyEvent int PROPERTY_EVENT_ERROR
-android.car.hardware.property CarPropertyEvent Parcelable.Creator<CarPropertyEvent> CREATOR
-android.car.hardware.property CarPropertyEvent int getEventType()
-android.car.hardware.property CarPropertyEvent CarPropertyValue<?> getCarPropertyValue()
-android.car.hardware.property CarPropertyEvent int describeContents()
-android.car.hardware.property CarPropertyEvent void writeToParcel(Parcel dest, int flags)
+android.car.hardware.power PowerComponentUtil boolean hasComponents(CarPowerPolicy policy, CarPowerPolicyFilter filter)
+android.car.hardware.power PowerComponentUtil boolean isValidPowerComponent(int component)
+android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_DISABLED
+android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_ENABLED
+android.car.hardware.power PowerComponentUtil int COMPONENT_STATE_UNTOUCHED
+android.car.hardware.power PowerComponentUtil int FIRST_POWER_COMPONENT
+android.car.hardware.power PowerComponentUtil int INVALID_POWER_COMPONENT
+android.car.hardware.power PowerComponentUtil int LAST_POWER_COMPONENT
+android.car.hardware.power PowerComponentUtil int toPowerComponent(String componentArg, boolean prefix)
android.car.hardware.property CarPropertyEvent CarPropertyEvent createErrorEventWithErrorCode(int propertyId, int areaId, int errorCode)
-android.car.hardware.property CarPropertyEvent int getErrorCode()
+android.car.hardware.property CarPropertyEvent CarPropertyValue<?> getCarPropertyValue()
+android.car.hardware.property CarPropertyEvent Parcelable.Creator<CarPropertyEvent> CREATOR
android.car.hardware.property CarPropertyEvent String toString()
android.car.hardware.property CarPropertyEvent boolean equals(Object object)
+android.car.hardware.property CarPropertyEvent int PROPERTY_EVENT_ERROR
+android.car.hardware.property CarPropertyEvent int PROPERTY_EVENT_PROPERTY_CHANGE
+android.car.hardware.property CarPropertyEvent int describeContents()
+android.car.hardware.property CarPropertyEvent int getErrorCode()
+android.car.hardware.property CarPropertyEvent int getEventType()
android.car.hardware.property CarPropertyEvent int hashCode()
+android.car.hardware.property CarPropertyEvent void writeToParcel(Parcel dest, int flags)
android.car.hardware.property CarPropertyManager String getReadPermission(int propId)
android.car.hardware.property CarPropertyManager String getWritePermission(int propId)
android.car.hardware.property CarPropertyManager void onCarDisconnected()
-android.car.hardware.property VehicleHalStatusCode int STATUS_OK
-android.car.hardware.property VehicleHalStatusCode int STATUS_TRY_AGAIN
-android.car.hardware.property VehicleHalStatusCode int STATUS_INVALID_ARG
-android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE
+android.car.hardware.property VehicleHalStatusCode String toString(int vehicleHalStatusCode)
android.car.hardware.property VehicleHalStatusCode int STATUS_ACCESS_DENIED
android.car.hardware.property VehicleHalStatusCode int STATUS_INTERNAL_ERROR
+android.car.hardware.property VehicleHalStatusCode int STATUS_INVALID_ARG
+android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE
android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_DISABLED
-android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_SPEED_LOW
-android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_SPEED_HIGH
android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_POOR_VISIBILITY
android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_SAFETY
-android.car.hardware.property VehicleHalStatusCode String toString(int vehicleHalStatusCode)
+android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_SPEED_HIGH
+android.car.hardware.property VehicleHalStatusCode int STATUS_NOT_AVAILABLE_SPEED_LOW
+android.car.hardware.property VehicleHalStatusCode int STATUS_OK
+android.car.hardware.property VehicleHalStatusCode int STATUS_TRY_AGAIN
android.car.input CarInputManager void onCarDisconnected()
-android.car.media CarAudioManager int INVALID_VOLUME_GROUP_ID
android.car.media CarAudioManager String AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
-android.car.media CarAudioManager boolean isDynamicRoutingEnabled()
-android.car.media CarAudioManager int getZoneIdForUid(int uid)
-android.car.media CarAudioManager boolean setZoneIdForUid(int zoneId, int uid)
android.car.media CarAudioManager boolean clearZoneIdForUid(int uid)
+android.car.media CarAudioManager boolean isDynamicRoutingEnabled()
+android.car.media CarAudioManager boolean setZoneIdForUid(int zoneId, int uid)
+android.car.media CarAudioManager int INVALID_VOLUME_GROUP_ID
+android.car.media CarAudioManager int getZoneIdForUid(int uid)
android.car.media CarAudioManager void onCarDisconnected()
-android.car.media CarAudioPatchHandle String getSourceAddress()
android.car.media CarAudioPatchHandle String getSinkAddress()
+android.car.media CarAudioPatchHandle String getSourceAddress()
android.car.media CarAudioPatchHandle int getHandleId()
-android.car.media CarMediaManager void onCarDisconnected()
android.car.media CarMediaManager boolean isIndependentPlaybackConfig()
+android.car.media CarMediaManager void onCarDisconnected()
android.car.media CarMediaManager void setIndependentPlaybackConfig(boolean independent)
android.car.navigation CarNavigationInstrumentCluster Bundle getExtra()
android.car.navigation CarNavigationStatusManager void onCarDisconnected()
-android.car.occupantawareness DriverMonitoringDetection int confidenceLevel
-android.car.occupantawareness DriverMonitoringDetection boolean isLookingOnRoad
-android.car.occupantawareness DriverMonitoringDetection long gazeDurationMillis
android.car.occupantawareness DriverMonitoringDetection Parcelable.Creator<DriverMonitoringDetection> CREATOR
-android.car.occupantawareness DriverMonitoringDetection int describeContents()
android.car.occupantawareness DriverMonitoringDetection String toString()
+android.car.occupantawareness DriverMonitoringDetection boolean isLookingOnRoad
+android.car.occupantawareness DriverMonitoringDetection int confidenceLevel
+android.car.occupantawareness DriverMonitoringDetection int describeContents()
+android.car.occupantawareness DriverMonitoringDetection long gazeDurationMillis
android.car.occupantawareness DriverMonitoringDetection void writeToParcel(Parcel dest, int flags)
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_UNKNOWN
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_CENTER_INSTRUMENT_CLUSTER
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_REAR_VIEW_MIRROR
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_LEFT_SIDE_MIRROR
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_RIGHT_SIDE_MIRROR
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_FORWARD_ROADWAY
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_LEFT_ROADWAY
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_RIGHT_ROADWAY
-android.car.occupantawareness GazeDetection int VEHICLE_REGION_HEAD_UNIT_DISPLAY
-android.car.occupantawareness GazeDetection int confidenceLevel
+android.car.occupantawareness GazeDetection Parcelable.Creator<GazeDetection> CREATOR
+android.car.occupantawareness GazeDetection Point3D gazeAngleUnitVector
+android.car.occupantawareness GazeDetection Point3D headAngleUnitVector
android.car.occupantawareness GazeDetection Point3D leftEyePosition
android.car.occupantawareness GazeDetection Point3D rightEyePosition
-android.car.occupantawareness GazeDetection Point3D headAngleUnitVector
-android.car.occupantawareness GazeDetection Point3D gazeAngleUnitVector
+android.car.occupantawareness GazeDetection String toString()
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_CENTER_INSTRUMENT_CLUSTER
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_FORWARD_ROADWAY
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_HEAD_UNIT_DISPLAY
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_LEFT_ROADWAY
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_LEFT_SIDE_MIRROR
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_REAR_VIEW_MIRROR
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_RIGHT_ROADWAY
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_RIGHT_SIDE_MIRROR
+android.car.occupantawareness GazeDetection int VEHICLE_REGION_UNKNOWN
+android.car.occupantawareness GazeDetection int confidenceLevel
+android.car.occupantawareness GazeDetection int describeContents()
android.car.occupantawareness GazeDetection int gazeTarget
android.car.occupantawareness GazeDetection long durationOnTargetMillis
-android.car.occupantawareness GazeDetection Parcelable.Creator<GazeDetection> CREATOR
-android.car.occupantawareness GazeDetection int describeContents()
android.car.occupantawareness GazeDetection void writeToParcel(Parcel dest, int flags)
-android.car.occupantawareness GazeDetection String toString()
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_NONE
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_DRIVER
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_FRONT_PASSENGER
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_LEFT
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_CENTER
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_LEFT
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_CENTER
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_RIGHT
+android.car.occupantawareness OccupantAwarenessDetection DriverMonitoringDetection driverMonitoringDetection
+android.car.occupantawareness OccupantAwarenessDetection GazeDetection gazeDetection
+android.car.occupantawareness OccupantAwarenessDetection Parcelable.Creator<OccupantAwarenessDetection> CREATOR
+android.car.occupantawareness OccupantAwarenessDetection String toString()
+android.car.occupantawareness OccupantAwarenessDetection boolean isPresent
+android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_HIGH
+android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_LOW
+android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_MAX
+android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_NONE
android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ALL_FRONT_OCCUPANTS
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ALL_OCCUPANTS
android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ALL_ROW_2_OCCUPANTS
android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ALL_ROW_3_OCCUPANTS
-android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ALL_OCCUPANTS
-android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_NONE
-android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_LOW
-android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_HIGH
-android.car.occupantawareness OccupantAwarenessDetection int CONFIDENCE_LEVEL_MAX
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_DRIVER
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_FRONT_PASSENGER
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_NONE
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_CENTER
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_LEFT
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_CENTER
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_LEFT
+android.car.occupantawareness OccupantAwarenessDetection int VEHICLE_OCCUPANT_ROW_3_PASSENGER_RIGHT
+android.car.occupantawareness OccupantAwarenessDetection int describeContents()
android.car.occupantawareness OccupantAwarenessDetection int role
android.car.occupantawareness OccupantAwarenessDetection long timestampMillis
-android.car.occupantawareness OccupantAwarenessDetection boolean isPresent
-android.car.occupantawareness OccupantAwarenessDetection GazeDetection gazeDetection
-android.car.occupantawareness OccupantAwarenessDetection DriverMonitoringDetection driverMonitoringDetection
-android.car.occupantawareness OccupantAwarenessDetection Parcelable.Creator<OccupantAwarenessDetection> CREATOR
-android.car.occupantawareness OccupantAwarenessDetection int describeContents()
android.car.occupantawareness OccupantAwarenessDetection void writeToParcel(Parcel dest, int flags)
-android.car.occupantawareness OccupantAwarenessDetection String toString()
-android.car.occupantawareness OccupantAwarenessManager void onCarDisconnected()
android.car.occupantawareness OccupantAwarenessManager int getCapabilityForRole(int role)
+android.car.occupantawareness OccupantAwarenessManager void onCarDisconnected()
android.car.occupantawareness OccupantAwarenessManager void registerChangeCallback(ChangeCallback callback)
android.car.occupantawareness OccupantAwarenessManager void unregisterChangeCallback()
+android.car.occupantawareness Point3D Parcelable.Creator<Point3D> CREATOR
+android.car.occupantawareness Point3D String toString()
android.car.occupantawareness Point3D double x
android.car.occupantawareness Point3D double y
android.car.occupantawareness Point3D double z
-android.car.occupantawareness Point3D Parcelable.Creator<Point3D> CREATOR
android.car.occupantawareness Point3D int describeContents()
android.car.occupantawareness Point3D void writeToParcel(Parcel dest, int flags)
-android.car.occupantawareness Point3D String toString()
-android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_READY
-android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_NOT_SUPPORTED
-android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_NOT_READY
-android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_SYSTEM_FAILURE
+android.car.occupantawareness SystemStatusEvent Parcelable.Creator<SystemStatusEvent> CREATOR
+android.car.occupantawareness SystemStatusEvent String toString()
+android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_DRIVER_MONITORING
+android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_GAZE
android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_NONE
android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_PRESENCE
-android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_GAZE
-android.car.occupantawareness SystemStatusEvent int DETECTION_TYPE_DRIVER_MONITORING
-android.car.occupantawareness SystemStatusEvent int systemStatus
-android.car.occupantawareness SystemStatusEvent int detectionType
-android.car.occupantawareness SystemStatusEvent Parcelable.Creator<SystemStatusEvent> CREATOR
+android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_NOT_READY
+android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_NOT_SUPPORTED
+android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_READY
+android.car.occupantawareness SystemStatusEvent int SYSTEM_STATUS_SYSTEM_FAILURE
android.car.occupantawareness SystemStatusEvent int describeContents()
+android.car.occupantawareness SystemStatusEvent int detectionType
+android.car.occupantawareness SystemStatusEvent int systemStatus
android.car.occupantawareness SystemStatusEvent void writeToParcel(Parcel dest, int flags)
-android.car.occupantawareness SystemStatusEvent String toString()
android.car.occupantconnection CarOccupantConnectionManager void onCarDisconnected()
-android.car.os CarPerformanceManager void onCarDisconnected()
android.car.os CarPerformanceManager void addCpuAvailabilityChangeListener(Executor executor, CpuAvailabilityMonitoringConfig config, CpuAvailabilityChangeListener listener)
+android.car.os CarPerformanceManager void onCarDisconnected()
android.car.os CarPerformanceManager void removeCpuAvailabilityChangeListener(Executor executor, CpuAvailabilityChangeListener listener)
android.car.os CarPerformanceManager.CpuAvailabilityChangeListener void onCpuAvailabilityChange(CpuAvailabilityInfo info)
android.car.os CpuAvailabilityInfo Parcelable.Creator<CpuAvailabilityInfo> CREATOR
-android.car.os CpuAvailabilityInfo int getCpuset()
-android.car.os CpuAvailabilityInfo int getAverageAvailabilityPercent()
-android.car.os CpuAvailabilityInfo boolean isTimeout()
android.car.os CpuAvailabilityInfo String toString()
-android.car.os CpuAvailabilityInfo void writeToParcel(android.os.Parcel dest, int flags)
+android.car.os CpuAvailabilityInfo boolean isTimeout()
android.car.os CpuAvailabilityInfo int describeContents()
-android.car.os CpuAvailabilityInfo.Builder Builder setCpuset(int value)
+android.car.os CpuAvailabilityInfo int getAverageAvailabilityPercent()
+android.car.os CpuAvailabilityInfo int getCpuset()
+android.car.os CpuAvailabilityInfo void writeToParcel(android.os.Parcel dest, int flags)
android.car.os CpuAvailabilityInfo.Builder Builder setAverageAvailabilityPercent(int value)
+android.car.os CpuAvailabilityInfo.Builder Builder setCpuset(int value)
android.car.os CpuAvailabilityInfo.Builder Builder setTimeout(boolean value)
android.car.os CpuAvailabilityInfo.Builder CpuAvailabilityInfo build()
+android.car.os CpuAvailabilityMonitoringConfig Parcelable.Creator<CpuAvailabilityMonitoringConfig> CREATOR
+android.car.os CpuAvailabilityMonitoringConfig String cpusetToString(int value)
+android.car.os CpuAvailabilityMonitoringConfig String ignorePercentToString(int value)
+android.car.os CpuAvailabilityMonitoringConfig String timeoutActionToString(int value)
+android.car.os CpuAvailabilityMonitoringConfig String toString()
android.car.os CpuAvailabilityMonitoringConfig int CPUSET_ALL
android.car.os CpuAvailabilityMonitoringConfig int CPUSET_BACKGROUND
android.car.os CpuAvailabilityMonitoringConfig int IGNORE_PERCENT_LOWER_BOUND
@@ -530,45 +542,40 @@
android.car.os CpuAvailabilityMonitoringConfig int MONITORING_TIMEOUT_NEVER
android.car.os CpuAvailabilityMonitoringConfig int TIMEOUT_ACTION_NOTIFICATION
android.car.os CpuAvailabilityMonitoringConfig int TIMEOUT_ACTION_REMOVE
-android.car.os CpuAvailabilityMonitoringConfig Parcelable.Creator<CpuAvailabilityMonitoringConfig> CREATOR
-android.car.os CpuAvailabilityMonitoringConfig String cpusetToString(int value)
-android.car.os CpuAvailabilityMonitoringConfig String ignorePercentToString(int value)
-android.car.os CpuAvailabilityMonitoringConfig String timeoutActionToString(int value)
+android.car.os CpuAvailabilityMonitoringConfig int describeContents()
android.car.os CpuAvailabilityMonitoringConfig int getCpuset()
android.car.os CpuAvailabilityMonitoringConfig int getLowerBoundPercent()
+android.car.os CpuAvailabilityMonitoringConfig int getTimeoutAction()
android.car.os CpuAvailabilityMonitoringConfig int getUpperBoundPercent()
android.car.os CpuAvailabilityMonitoringConfig long getTimeoutInSeconds()
-android.car.os CpuAvailabilityMonitoringConfig int getTimeoutAction()
-android.car.os CpuAvailabilityMonitoringConfig String toString()
android.car.os CpuAvailabilityMonitoringConfig void writeToParcel(android.os.Parcel dest, int flags)
-android.car.os CpuAvailabilityMonitoringConfig int describeContents()
android.car.os ThreadPolicyWithPriority String priorityToString(int value)
android.car.os ThreadPolicyWithPriority String schedToString(int value)
android.car.projection ProjectionOptions Builder builder()
android.car.projection ProjectionOptions String toString()
+android.car.projection ProjectionOptions.Builder Builder setAccessPointMode(int accessPointMode)
+android.car.projection ProjectionOptions.Builder Builder setConsentActivity(ComponentName consentActivity)
android.car.projection ProjectionOptions.Builder Builder setProjectionActivityOptions(ActivityOptions activityOptions)
android.car.projection ProjectionOptions.Builder Builder setUiMode(int uiMode)
-android.car.projection ProjectionOptions.Builder Builder setConsentActivity(ComponentName consentActivity)
-android.car.projection ProjectionOptions.Builder Builder setAccessPointMode(int accessPointMode)
android.car.projection ProjectionOptions.Builder ProjectionOptions build()
android.car.remoteaccess CarRemoteAccessManager void onCarDisconnected()
-android.car.settings CarSettings int[] DEFAULT_GARAGE_MODE_WAKE_UP_TIME
android.car.settings CarSettings int DEFAULT_GARAGE_MODE_MAINTENANCE_WINDOW
+android.car.settings CarSettings int[] DEFAULT_GARAGE_MODE_WAKE_UP_TIME
android.car.settings CarSettings.Global String DEFAULT_USER_RESTRICTIONS_SET
android.car.settings CarSettings.Global String DISABLE_INSTRUMENTATION_SERVICE
android.car.settings CarSettings.Global String ENABLE_USER_SWITCH_DEVELOPER_MESSAGE
-android.car.settings CarSettings.Global String LAST_ACTIVE_USER_ID
android.car.settings CarSettings.Global String LAST_ACTIVE_PERSISTENT_USER_ID
+android.car.settings CarSettings.Global String LAST_ACTIVE_USER_ID
android.car.settings CarSettings.Global String SYSTEM_BAR_VISIBILITY_OVERRIDE
android.car.settings CarSettings.Secure String KEY_BLUETOOTH_DEVICES
android.car.settings CarSettings.Secure String KEY_BLUETOOTH_PROFILES_INHIBITED
android.car.storagemonitoring CarStorageMonitoringManager void onCarDisconnected()
android.car.storagemonitoring IoStats void writeToJson(JsonWriter jsonWriter)
-android.car.storagemonitoring IoStatsEntry void writeToJson(JsonWriter jsonWriter)
android.car.storagemonitoring IoStatsEntry IoStatsEntry delta(IoStatsEntry other)
android.car.storagemonitoring IoStatsEntry boolean representsSameMetrics(UidIoRecord record)
-android.car.storagemonitoring IoStatsEntry.Metrics void writeToJson(JsonWriter jsonWriter)
+android.car.storagemonitoring IoStatsEntry void writeToJson(JsonWriter jsonWriter)
android.car.storagemonitoring IoStatsEntry.Metrics Metrics delta(Metrics other)
+android.car.storagemonitoring IoStatsEntry.Metrics void writeToJson(JsonWriter jsonWriter)
android.car.storagemonitoring LifetimeWriteInfo void writeToJson(JsonWriter jsonWriter)
android.car.storagemonitoring UidIoRecord UidIoRecord delta(IoStatsEntry other)
android.car.storagemonitoring UidIoRecord UidIoRecord delta(UidIoRecord other)
@@ -576,155 +583,155 @@
android.car.storagemonitoring WearEstimate void writeToJson(JsonWriter jsonWriter)
android.car.telemetry CarTelemetryManager void onCarDisconnected()
android.car.test CarLocationTestHelper boolean injectLocation(Location location, Context context)
-android.car.test CarTestManager void onCarDisconnected()
-android.car.test CarTestManager void stopCarService(IBinder token)
-android.car.test CarTestManager void startCarService(IBinder token)
android.car.test CarTestManager String dumpVhal(List<String> options, long waitTimeoutMs)
-android.car.test CarTestManager boolean hasAidlVhal()
android.car.test CarTestManager String getOemServiceName()
-android.car.user CarUserManager String TAG
-android.car.user CarUserManager int USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED
+android.car.test CarTestManager boolean hasAidlVhal()
+android.car.test CarTestManager void onCarDisconnected()
+android.car.test CarTestManager void startCarService(IBinder token)
+android.car.test CarTestManager void stopCarService(IBinder token)
+android.car.user CarUserManager AsyncFuture<UserCreationResult> createGuest(String name)
+android.car.user CarUserManager AsyncFuture<UserCreationResult> createUser(String name, int flags)
+android.car.user CarUserManager AsyncFuture<UserIdentificationAssociationResponse> setUserIdentificationAssociation(int[] types, int[] values)
+android.car.user CarUserManager AsyncFuture<UserSwitchResult> logoutUser()
+android.car.user CarUserManager AsyncFuture<UserSwitchResult> switchUser(int targetUserId)
android.car.user CarUserManager String BUNDLE_PARAM_ACTION
android.car.user CarUserManager String BUNDLE_PARAM_PREVIOUS_USER_ID
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_KEY_FOB
+android.car.user CarUserManager String TAG
+android.car.user CarUserManager UserIdentificationAssociationResponse getUserIdentificationAssociation(int types)
+android.car.user CarUserManager UserRemovalResult removeUser(int userId)
+android.car.user CarUserManager boolean isUserHalUserAssociationSupported()
+android.car.user CarUserManager boolean isValidUser(int userId)
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_ASSOCIATE_CURRENT_USER
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_ALL_USERS
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_CURRENT_USER
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_1
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_2
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_3
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_4
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_ASSOCIATE_CURRENT_USER
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_CURRENT_USER
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_ALL_USERS
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_UNKNOWN
-android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATE_CURRENT_USER
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_TYPE_KEY_FOB
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATED_ANOTHER_USER
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATE_CURRENT_USER
android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_NOT_ASSOCIATED_ANY_USER
-android.car.user CarUserManager AsyncFuture<UserSwitchResult> switchUser(int targetUserId)
-android.car.user CarUserManager AsyncFuture<UserSwitchResult> logoutUser()
-android.car.user CarUserManager AsyncFuture<UserCreationResult> createGuest(String name)
-android.car.user CarUserManager AsyncFuture<UserCreationResult> createUser(String name, int flags)
-android.car.user CarUserManager void updatePreCreatedUsers()
-android.car.user CarUserManager UserRemovalResult removeUser(int userId)
-android.car.user CarUserManager boolean isUserHalUserAssociationSupported()
-android.car.user CarUserManager UserIdentificationAssociationResponse getUserIdentificationAssociation(int types)
-android.car.user CarUserManager AsyncFuture<UserIdentificationAssociationResponse> setUserIdentificationAssociation(int[] types, int[] values)
-android.car.user CarUserManager void setUserSwitchUiCallback(UserSwitchUiCallback callback)
+android.car.user CarUserManager int USER_IDENTIFICATION_ASSOCIATION_VALUE_UNKNOWN
+android.car.user CarUserManager int USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED
android.car.user CarUserManager void onCarDisconnected()
-android.car.user CarUserManager boolean isValidUser(int userId)
-android.car.user CarUserManager.UserLifecycleEvent int getUserId()
+android.car.user CarUserManager void setUserSwitchUiCallback(UserSwitchUiCallback callback)
+android.car.user CarUserManager void updatePreCreatedUsers()
android.car.user CarUserManager.UserLifecycleEvent int getPreviousUserId()
+android.car.user CarUserManager.UserLifecycleEvent int getUserId()
android.car.user CarUserManager.UserSwitchUiCallback void showUserSwitchDialog(int userId)
-android.car.user ExperimentalCarUserManager String TAG
android.car.user ExperimentalCarUserManager AndroidFuture<UserCreationResult> createDriver(String name, boolean admin)
-android.car.user ExperimentalCarUserManager int createPassenger(String name, int driverId)
android.car.user ExperimentalCarUserManager AndroidFuture<UserSwitchResult> switchDriver(int driverId)
android.car.user ExperimentalCarUserManager List<Integer> getAllDrivers()
android.car.user ExperimentalCarUserManager List<Integer> getPassengers(int driverId)
+android.car.user ExperimentalCarUserManager String TAG
android.car.user ExperimentalCarUserManager boolean startPassenger(int passengerId, int zoneId)
android.car.user ExperimentalCarUserManager boolean stopPassenger(int passengerId)
+android.car.user ExperimentalCarUserManager int createPassenger(String name, int driverId)
android.car.user ExperimentalCarUserManager void onCarDisconnected()
android.car.user OperationResult boolean isSuccess()
android.car.user UserIdentificationAssociationResponse Parcelable.Creator<UserIdentificationAssociationResponse> CREATOR
+android.car.user UserIdentificationAssociationResponse String getErrorMessage()
+android.car.user UserIdentificationAssociationResponse String toString()
android.car.user UserIdentificationAssociationResponse UserIdentificationAssociationResponse forFailure()
android.car.user UserIdentificationAssociationResponse UserIdentificationAssociationResponse forFailure(String errorMessage)
android.car.user UserIdentificationAssociationResponse UserIdentificationAssociationResponse forSuccess(int[] values)
android.car.user UserIdentificationAssociationResponse UserIdentificationAssociationResponse forSuccess(int[] values, String errorMessage)
android.car.user UserIdentificationAssociationResponse boolean isSuccess()
-android.car.user UserIdentificationAssociationResponse String getErrorMessage()
-android.car.user UserIdentificationAssociationResponse int[] getValues()
-android.car.user UserIdentificationAssociationResponse String toString()
-android.car.user UserIdentificationAssociationResponse void writeToParcel(android.os.Parcel dest, int flags)
android.car.user UserIdentificationAssociationResponse int describeContents()
+android.car.user UserIdentificationAssociationResponse int[] getValues()
+android.car.user UserIdentificationAssociationResponse void writeToParcel(android.os.Parcel dest, int flags)
android.car.user UserLifecycleEventFilter int[] getEventTypes()
android.car.user UserLifecycleEventFilter int[] getUserIds()
-android.car.user UserStartResult int STATUS_SUCCESSFUL
+android.car.user UserStartResult Parcelable.Creator<UserStartResult> CREATOR
+android.car.user UserStartResult String statusToString(int value)
+android.car.user UserStartResult String toString()
+android.car.user UserStartResult boolean isSuccess()
android.car.user UserStartResult int STATUS_ANDROID_FAILURE
+android.car.user UserStartResult int STATUS_SUCCESSFUL
android.car.user UserStartResult int STATUS_SUCCESSFUL_USER_IS_CURRENT_USER
android.car.user UserStartResult int STATUS_USER_DOES_NOT_EXIST
-android.car.user UserStartResult Parcelable.Creator<UserStartResult> CREATOR
-android.car.user UserStartResult boolean isSuccess()
-android.car.user UserStartResult String statusToString(int value)
-android.car.user UserStartResult int getStatus()
-android.car.user UserStartResult String toString()
-android.car.user UserStartResult void writeToParcel(android.os.Parcel dest, int flags)
android.car.user UserStartResult int describeContents()
-android.car.user UserStopResult int STATUS_SUCCESSFUL
-android.car.user UserStopResult int STATUS_ANDROID_FAILURE
-android.car.user UserStopResult int STATUS_USER_DOES_NOT_EXIST
-android.car.user UserStopResult int STATUS_FAILURE_SYSTEM_USER
-android.car.user UserStopResult int STATUS_FAILURE_CURRENT_USER
+android.car.user UserStartResult int getStatus()
+android.car.user UserStartResult void writeToParcel(android.os.Parcel dest, int flags)
android.car.user UserStopResult Parcelable.Creator<UserStopResult> CREATOR
-android.car.user UserStopResult boolean isSuccess(int status)
-android.car.user UserStopResult boolean isSuccess()
android.car.user UserStopResult String statusToString(int value)
-android.car.user UserStopResult int getStatus()
android.car.user UserStopResult String toString()
-android.car.user UserStopResult void writeToParcel(android.os.Parcel dest, int flags)
+android.car.user UserStopResult boolean isSuccess()
+android.car.user UserStopResult boolean isSuccess(int status)
+android.car.user UserStopResult int STATUS_ANDROID_FAILURE
+android.car.user UserStopResult int STATUS_FAILURE_CURRENT_USER
+android.car.user UserStopResult int STATUS_FAILURE_SYSTEM_USER
+android.car.user UserStopResult int STATUS_SUCCESSFUL
+android.car.user UserStopResult int STATUS_USER_DOES_NOT_EXIST
android.car.user UserStopResult int describeContents()
+android.car.user UserStopResult int getStatus()
+android.car.user UserStopResult void writeToParcel(android.os.Parcel dest, int flags)
+android.car.util.concurrent AndroidAsyncFuture AsyncFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
android.car.util.concurrent AndroidAsyncFuture T get()
android.car.util.concurrent AndroidAsyncFuture T get(long timeout, TimeUnit unit)
-android.car.util.concurrent AndroidAsyncFuture AsyncFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
-android.car.util.concurrent AndroidFuture Parcelable.Creator<AndroidFuture> CREATOR
-android.car.util.concurrent AndroidFuture AndroidFuture<U> completedFuture(U value)
-android.car.util.concurrent AndroidFuture boolean complete(T value)
-android.car.util.concurrent AndroidFuture boolean completeExceptionally(Throwable ex)
-android.car.util.concurrent AndroidFuture boolean cancel(boolean mayInterruptIfRunning)
-android.car.util.concurrent AndroidFuture void onCompleted(T res, Throwable err)
-android.car.util.concurrent AndroidFuture AndroidFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
-android.car.util.concurrent AndroidFuture AndroidFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
-android.car.util.concurrent AndroidFuture AndroidFuture<T> orTimeout(long timeout, TimeUnit unit)
android.car.util.concurrent AndroidFuture AndroidFuture<T> cancelTimeout()
+android.car.util.concurrent AndroidFuture AndroidFuture<T> orTimeout(long timeout, TimeUnit unit)
android.car.util.concurrent AndroidFuture AndroidFuture<T> setTimeoutHandler(Handler h)
-android.car.util.concurrent AndroidFuture AndroidFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
-android.car.util.concurrent AndroidFuture AndroidFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
-android.car.util.concurrent AndroidFuture AndroidFuture<U> thenApply(Function<? super T,? extends U> fn)
-android.car.util.concurrent AndroidFuture AndroidFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
-android.car.util.concurrent AndroidFuture AndroidFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> combineResults)
-android.car.util.concurrent AndroidFuture AndroidFuture<T> thenCombine(CompletionStage<Void> other)
android.car.util.concurrent AndroidFuture AndroidFuture<T> supply(Supplier<T> supplier)
android.car.util.concurrent AndroidFuture AndroidFuture<T> supplyAsync(Supplier<T> supplier, Executor executor)
-android.car.util.concurrent AndroidFuture void writeToParcel(Parcel dest, int flags)
+android.car.util.concurrent AndroidFuture AndroidFuture<T> thenCombine(CompletionStage<Void> other)
+android.car.util.concurrent AndroidFuture AndroidFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
+android.car.util.concurrent AndroidFuture AndroidFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
+android.car.util.concurrent AndroidFuture AndroidFuture<U> completedFuture(U value)
+android.car.util.concurrent AndroidFuture AndroidFuture<U> thenApply(Function<? super T,? extends U> fn)
+android.car.util.concurrent AndroidFuture AndroidFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
+android.car.util.concurrent AndroidFuture AndroidFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
+android.car.util.concurrent AndroidFuture AndroidFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
+android.car.util.concurrent AndroidFuture AndroidFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> combineResults)
+android.car.util.concurrent AndroidFuture Parcelable.Creator<AndroidFuture> CREATOR
+android.car.util.concurrent AndroidFuture boolean cancel(boolean mayInterruptIfRunning)
+android.car.util.concurrent AndroidFuture boolean complete(T value)
+android.car.util.concurrent AndroidFuture boolean completeExceptionally(Throwable ex)
android.car.util.concurrent AndroidFuture int describeContents()
+android.car.util.concurrent AndroidFuture void onCompleted(T res, Throwable err)
+android.car.util.concurrent AndroidFuture void writeToParcel(Parcel dest, int flags)
+android.car.util.concurrent AsyncFuture AsyncFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
android.car.util.concurrent AsyncFuture T get()
android.car.util.concurrent AsyncFuture T get(long timeout, TimeUnit unit)
-android.car.util.concurrent AsyncFuture AsyncFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
android.car.vms VmsClient void register()
android.car.vms VmsClient void unregister()
android.car.vms VmsClientManager void onCarDisconnected()
android.car.vms VmsOperationRecorder.Writer boolean isEnabled()
android.car.vms VmsOperationRecorder.Writer void write(String msg)
android.car.vms VmsProviderInfo Parcelable.Creator<VmsProviderInfo> CREATOR
-android.car.vms VmsProviderInfo byte[] getDescription()
android.car.vms VmsProviderInfo boolean equals(Object o)
+android.car.vms VmsProviderInfo byte[] getDescription()
+android.car.vms VmsProviderInfo int describeContents()
android.car.vms VmsProviderInfo int hashCode()
android.car.vms VmsProviderInfo void writeToParcel(Parcel dest, int flags)
-android.car.vms VmsProviderInfo int describeContents()
android.car.vms VmsPublisherClientService void onCarLifecycleChanged(Car car, boolean ready)
android.car.vms VmsRegistrationInfo Parcelable.Creator<VmsRegistrationInfo> CREATOR
android.car.vms VmsRegistrationInfo VmsAvailableLayers getAvailableLayers()
android.car.vms VmsRegistrationInfo VmsSubscriptionState getSubscriptionState()
android.car.vms VmsRegistrationInfo boolean equals(Object o)
+android.car.vms VmsRegistrationInfo int describeContents()
android.car.vms VmsRegistrationInfo int hashCode()
android.car.vms VmsRegistrationInfo void writeToParcel(Parcel dest, int flags)
-android.car.vms VmsRegistrationInfo int describeContents()
android.car.vms VmsSubscriberManager VmsSubscriberManager wrap(Car car, VmsClientManager clientManager)
android.car.vms VmsSubscriberManager void onCarDisconnected()
+android.car.vms VmsSubscriptionHelper Set<VmsAssociatedLayer> getSubscriptions()
android.car.vms VmsSubscriptionHelper void subscribe(VmsLayer layer)
android.car.vms VmsSubscriptionHelper void subscribe(VmsLayer layer, int providerId)
android.car.vms VmsSubscriptionHelper void unsubscribe(VmsLayer layer)
android.car.vms VmsSubscriptionHelper void unsubscribe(VmsLayer layer, int providerId)
-android.car.vms VmsSubscriptionHelper Set<VmsAssociatedLayer> getSubscriptions()
android.car.watchdog CarWatchdogManager void onCarDisconnected()
-android.car.watchdog IoOveruseStats.Builder Builder setStartTime(long value)
android.car.watchdog IoOveruseStats.Builder Builder setDurationInSeconds(long value)
-android.car.watchdog IoOveruseStats.Builder Builder setTotalOveruses(long value)
-android.car.watchdog IoOveruseStats.Builder Builder setTotalTimesKilled(long value)
-android.car.watchdog IoOveruseStats.Builder Builder setTotalBytesWritten(long value)
android.car.watchdog IoOveruseStats.Builder Builder setKillableOnOveruse(boolean value)
android.car.watchdog IoOveruseStats.Builder Builder setRemainingWriteBytes(PerStateBytes value)
+android.car.watchdog IoOveruseStats.Builder Builder setStartTime(long value)
+android.car.watchdog IoOveruseStats.Builder Builder setTotalBytesWritten(long value)
+android.car.watchdog IoOveruseStats.Builder Builder setTotalOveruses(long value)
+android.car.watchdog IoOveruseStats.Builder Builder setTotalTimesKilled(long value)
android.car.watchdog IoOveruseStats.Builder IoOveruseStats build()
android.car.watchdog PackageKillableState String killableStateToString(int value)
android.car.watchdog ResourceOveruseConfiguration String componentTypeToString(int value)
+android.car.watchdog ResourceOveruseStats.Builder Builder setIoOveruseStats(IoOveruseStats value)
android.car.watchdog ResourceOveruseStats.Builder Builder setPackageName(String value)
android.car.watchdog ResourceOveruseStats.Builder Builder setUserHandle(UserHandle value)
-android.car.watchdog ResourceOveruseStats.Builder Builder setIoOveruseStats(IoOveruseStats value)
android.car.watchdog ResourceOveruseStats.Builder ResourceOveruseStats build()
diff --git a/tests/carservice_unit_test/res/raw/valid_power_policy_custom_components.xml b/tests/carservice_unit_test/res/raw/valid_power_policy_custom_components.xml
index de0ad46..abe6473 100644
--- a/tests/carservice_unit_test/res/raw/valid_power_policy_custom_components.xml
+++ b/tests/carservice_unit_test/res/raw/valid_power_policy_custom_components.xml
@@ -15,13 +15,6 @@
-->
<powerPolicy version="1.0">
-
- <customComponents>
- <customComponent value="1000">CUSTOM_COMPONENT_1000</customComponent>
- <customComponent value="1003">CUSTOM_COMPONENT_SPECIAL_SENSOR</customComponent>
- <customComponent value="1002">CUSTOM_COMPONENT_AUX_INPUT</customComponent>
- </customComponents>
-
<policyGroups>
<policyGroup id="basic_policy_group">
<defaultPolicy state="WaitForVHAL" id="policy_id_other_on"/>
@@ -114,4 +107,9 @@
</policy>
</systemPolicyOverrides>
+ <customComponents>
+ <customComponent value="1000">CUSTOM_COMPONENT_1000</customComponent>
+ <customComponent value="1003">CUSTOM_COMPONENT_SPECIAL_SENSOR</customComponent>
+ <customComponent value="1002">CUSTOM_COMPONENT_AUX_INPUT</customComponent>
+ </customComponents>
</powerPolicy>
diff --git a/tests/carservice_unit_test/src/android/car/AnnotationTest.java b/tests/carservice_unit_test/src/android/car/AnnotationTest.java
deleted file mode 100644
index bed0487..0000000
--- a/tests/carservice_unit_test/src/android/car/AnnotationTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 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;
-
-import static android.car.test.util.AnnotationHelper.checkForAnnotation;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.car.annotation.AddedInOrBefore;
-import android.car.annotation.ApiRequirements;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.car.R;
-
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashSet;
-
-public final class AnnotationTest {
- private static final String TAG = AnnotationTest.class.getSimpleName();
-
- @Test
- public void testCarAPIApiRequirementsAnnotation() throws Exception {
- HashSet<String> addedInOrBeforeApis = readAddedInOrBeforeFile(
- R.raw.car_addedinorbefore_apis);
- assertThat(addedInOrBeforeApis).isNotEmpty();
- checkForAnnotation(readFile(R.raw.car_api_classes), addedInOrBeforeApis,
- ApiRequirements.class,
- AddedInOrBefore.class);
- }
-
- @Test
- public void testCarBuiltInAPIAddedInAnnotation() throws Exception {
- checkForAnnotation(readFile(R.raw.car_built_in_api_classes),
- android.car.builtin.annotation.AddedIn.class);
- }
-
- private String[] readFile(int resourceId) throws IOException {
- try (InputStream configurationStream = ApplicationProvider.getApplicationContext()
- .getResources().openRawResource(resourceId)) {
- return new String(configurationStream.readAllBytes()).split("\n");
- }
- }
-
- private HashSet<String> readAddedInOrBeforeFile(int resourceId) throws IOException {
- return new HashSet<>(Arrays.asList(readFile(resourceId)));
- }
-}
diff --git a/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleFactoryTest.java b/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleFactoryTest.java
new file mode 100644
index 0000000..b86edac
--- /dev/null
+++ b/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleFactoryTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class CarTaskViewControllerHostLifecycleFactoryTest {
+ private final FakeCarTaskViewControllerHostLifecycleObserver mObserver =
+ new FakeCarTaskViewControllerHostLifecycleObserver();
+ private TestActivity mActivity;
+
+ @Rule
+ public ActivityScenarioRule<TestActivity> activityRule = new ActivityScenarioRule<>(
+ TestActivity.class);
+
+ @Before
+ public void setup() {
+ activityRule.getScenario().onActivity(activity -> mActivity = activity);
+ }
+
+ @Test
+ public void forActivity_activityResumed_callsObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle =
+ CarTaskViewControllerHostLifecycleFactory.forActivity(mActivity);
+ lifecycle.registerObserver(mObserver);
+
+ // Act
+ activityRule.getScenario().moveToState(Lifecycle.State.CREATED);
+ activityRule.getScenario().moveToState(Lifecycle.State.RESUMED);
+
+ // Assert
+ assertThat(mObserver.mHostAppearedCalled).isTrue();
+ }
+
+ @Test
+ public void forActivity_activityStarted_callsObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle =
+ CarTaskViewControllerHostLifecycleFactory.forActivity(mActivity);
+ lifecycle.registerObserver(mObserver);
+
+ // Act
+ activityRule.getScenario().moveToState(Lifecycle.State.CREATED);
+ activityRule.getScenario().moveToState(Lifecycle.State.STARTED);
+
+ // Assert
+ assertThat(mObserver.mHostAppearedCalled).isTrue();
+ }
+
+ @Test
+ public void forActivity_activityStopped_callsObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle =
+ CarTaskViewControllerHostLifecycleFactory.forActivity(mActivity);
+ lifecycle.registerObserver(mObserver);
+
+ // Act
+ activityRule.getScenario().moveToState(Lifecycle.State.RESUMED);
+ activityRule.getScenario().moveToState(Lifecycle.State.CREATED);
+
+ // Assert
+ assertThat(mObserver.mHostDisappearedCalled).isTrue();
+ }
+
+ @Test
+ public void forActivity_activityDestroyed_callsObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle =
+ CarTaskViewControllerHostLifecycleFactory.forActivity(mActivity);
+ lifecycle.registerObserver(mObserver);
+
+ // Act
+ activityRule.getScenario().moveToState(Lifecycle.State.CREATED);
+ activityRule.getScenario().moveToState(Lifecycle.State.DESTROYED);
+
+ // Assert
+ assertThat(mObserver.mHostDestroyedCalled).isTrue();
+ }
+
+ public static class TestActivity extends Activity {
+ }
+}
diff --git a/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleTest.java b/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleTest.java
new file mode 100644
index 0000000..d606d61
--- /dev/null
+++ b/tests/carservice_unit_test/src/android/car/app/CarTaskViewControllerHostLifecycleTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class CarTaskViewControllerHostLifecycleTest {
+ @Test
+ public void hostAppeared_notifiesObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle = new CarTaskViewControllerHostLifecycle();
+ FakeCarTaskViewControllerHostLifecycleObserver observer =
+ new FakeCarTaskViewControllerHostLifecycleObserver();
+ lifecycle.registerObserver(observer);
+
+ // Act
+ lifecycle.hostAppeared();
+
+ // Assert
+ assertThat(observer.mHostAppearedCalled).isTrue();
+ }
+
+ @Test
+ public void hostAppeared_isVisibleReturnsTrue() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle = new CarTaskViewControllerHostLifecycle();
+
+ // Act
+ lifecycle.hostAppeared();
+
+ // Assert
+ assertThat(lifecycle.isVisible()).isTrue();
+ }
+
+ @Test
+ public void hostDisappeared_notifiesObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle = new CarTaskViewControllerHostLifecycle();
+ FakeCarTaskViewControllerHostLifecycleObserver observer =
+ new FakeCarTaskViewControllerHostLifecycleObserver();
+ lifecycle.registerObserver(observer);
+
+ // Act
+ lifecycle.hostDisappeared();
+
+ // Assert
+ assertThat(observer.mHostDisappearedCalled).isTrue();
+ }
+
+ @Test
+ public void hostDisappeared_isVisibleReturnsFalse() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle = new CarTaskViewControllerHostLifecycle();
+ lifecycle.hostAppeared();
+
+ // Act
+ lifecycle.hostDisappeared();
+
+ // Assert
+ assertThat(lifecycle.isVisible()).isFalse();
+ }
+
+ @Test
+ public void hostDestroyed_notifiesObserver() {
+ // Arrange
+ CarTaskViewControllerHostLifecycle lifecycle = new CarTaskViewControllerHostLifecycle();
+ FakeCarTaskViewControllerHostLifecycleObserver observer =
+ new FakeCarTaskViewControllerHostLifecycleObserver();
+ lifecycle.registerObserver(observer);
+
+ // Act
+ lifecycle.hostDestroyed();
+
+ // Assert
+ assertThat(observer.mHostDestroyedCalled).isTrue();
+ }
+}
diff --git a/tests/carservice_unit_test/src/android/car/app/FakeCarTaskViewControllerHostLifecycleObserver.java b/tests/carservice_unit_test/src/android/car/app/FakeCarTaskViewControllerHostLifecycleObserver.java
new file mode 100644
index 0000000..00a5752
--- /dev/null
+++ b/tests/carservice_unit_test/src/android/car/app/FakeCarTaskViewControllerHostLifecycleObserver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+class FakeCarTaskViewControllerHostLifecycleObserver implements
+ CarTaskViewControllerHostLifecycle.CarTaskViewControllerHostLifecycleObserver {
+ boolean mHostAppearedCalled = false;
+ boolean mHostDisappearedCalled = false;
+ boolean mHostDestroyedCalled = false;
+
+ @Override
+ public void onHostAppeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ this.mHostAppearedCalled = true;
+ }
+
+ @Override
+ public void onHostDisappeared(CarTaskViewControllerHostLifecycle lifecycle) {
+ this.mHostDisappearedCalled = true;
+ }
+
+ @Override
+ public void onHostDestroyed(CarTaskViewControllerHostLifecycle lifecycle) {
+ this.mHostDestroyedCalled = true;
+ }
+}
diff --git a/tests/carservice_unit_test/src/android/car/media/CarAudioManagerUnitTest.java b/tests/carservice_unit_test/src/android/car/media/CarAudioManagerUnitTest.java
index 90f84f9..d5e32ae 100644
--- a/tests/carservice_unit_test/src/android/car/media/CarAudioManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/android/car/media/CarAudioManagerUnitTest.java
@@ -835,13 +835,14 @@
@Test
public void onCarDisconnected() throws Exception {
- ArgumentCaptor<IBinder> captor = ArgumentCaptor.forClass(IBinder.class);
- mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
- verify(mServiceMock).registerVolumeCallback(captor.capture());
+ ICarVolumeCallback serviceVolCallback = getCarVolumeCallbackImpl(mVolumeCallbackMock1);
+ ICarVolumeEventCallback serviceVolEventCallback = getCarVolumeEventCallbackImpl(
+ mVolumeGroupEventCallbackMock1);
mCarAudioManager.onCarDisconnected();
- verify(mServiceMock).unregisterVolumeCallback(captor.getValue());
+ verify(mServiceMock).unregisterVolumeCallback(serviceVolCallback.asBinder());
+ verify(mServiceMock).unregisterCarVolumeEventCallback(serviceVolEventCallback);
}
@Test
@@ -1709,6 +1710,7 @@
private ICarVolumeEventCallback getCarVolumeEventCallbackImpl(CarVolumeGroupEventCallback
callbackMock) throws Exception {
+ when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, callbackMock);
ArgumentCaptor<ICarVolumeEventCallback> captor = ArgumentCaptor.forClass(
ICarVolumeEventCallback.class);
diff --git a/tests/carservice_unit_test/src/android/car/test/ApiCheckerRuleTest.java b/tests/carservice_unit_test/src/android/car/test/ApiCheckerRuleTest.java
deleted file mode 100644
index 92b4642..0000000
--- a/tests/carservice_unit_test/src/android/car/test/ApiCheckerRuleTest.java
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*
- * Copyright (C) 2022 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.test;
-
-import static android.car.test.JUnitHelper.newTestMethod;
-import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetCarVersion;
-import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetPlatformVersion;
-import static android.car.test.mocks.AndroidMockitoHelper.mockCarIsApiVersionAtLeast;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import android.car.Car;
-import android.car.CarVersion;
-import android.car.PlatformVersion;
-import android.car.PlatformVersionMismatchException;
-import android.car.annotation.AddedInOrBefore;
-import android.car.annotation.ApiRequirements;
-import android.car.test.ApiCheckerRule.ExpectedVersionAssumptionViolationException;
-import android.car.test.ApiCheckerRule.IgnoreInvalidApi;
-import android.car.test.ApiCheckerRule.IncompatibleApiRequirementsException;
-import android.car.test.ApiCheckerRule.PlatformVersionMismatchExceptionNotThrownException;
-import android.car.test.ApiCheckerRule.SupportedVersionTest;
-import android.car.test.ApiCheckerRule.UnsupportedVersionTest;
-import android.car.test.ApiCheckerRule.UnsupportedVersionTest.Behavior;
-import android.car.test.JUnitHelper.SimpleStatement;
-import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.os.Build;
-import android.util.Log;
-
-import com.android.compatibility.common.util.ApiTest;
-import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.NonApiTest;
-import com.android.compatibility.common.util.ReasonType;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.lang.annotation.Annotation;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-public final class ApiCheckerRuleTest extends AbstractExtendedMockitoTestCase {
-
- private static final String TAG = ApiCheckerRuleTest.class.getSimpleName();
-
- // Not a real test (i.e., it doesn't exist on this class), but it's passed to Description
- private static final String TEST_METHOD_BEING_EXECUTED = "testAmI..OrNot";
-
- // Similar, not a real method, but passed on annotation fields
- private static final String METHOD_NAME_INVALID = "invalidIAm";
-
- private static final String METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0 =
- "requiresCarAndPlatformTiramisu0";
- private static final String METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1 =
- "requiresCarAndPlatformTiramisu1";
-
- private static final String METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION =
- "annotatedWithSupportedVersionAnnotationWithRightUnsupportedField";
- private static final String METHOD_NAME_ANNOTATED_WITH_WRONG_SUPPORTED_VERSION_ANNOTATION =
- "annotatedWithSupportedVersionAnnotationWithWrongUnsupportedField";
- private static final String METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION =
- "annotatedWithUnsupportedVersionAnnotationWithRightSupportedField";
- private static final String METHOD_NAME_ANNOTATED_WITH_WRONG_UNSUPPORTED_VERSION_ANNOTATION =
- "annotatedWithUnsupportedVersionAnnotationWithWrongSupportedField";
-
- private static final String INVALID_API = "I.cant.believe.this.is.a.valid.API";
- private static final String VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0 =
- "android.car.test.ApiCheckerRuleTest#"
- + METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0;
- private static final String VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1 =
- "android.car.test.ApiCheckerRuleTest#"
- + METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- private static final String ANOTHER_VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0 =
- "android.car.test.ApiCheckerRuleTest#alsoRequiresCarAndPlatformTiramisu0";
- private static final String VALID_API_WITHOUT_ANNOTATIONS =
- "android.car.test.ApiCheckerRuleTest#apiYUNoAnnotated";
- private static final String VALID_API_ADDED_IN_OR_BEFORE =
- "android.car.test.ApiCheckerRuleTest#annotatedWithAddedInOrBefore";
-
- // Example justification for a NonApiTest.
- private static final String NON_API_TEST_JUSTIFICATION = "METRIC";
-
- // Example removal version annotations.
- private static final int SOFT_REMOVAL_VERSION = 35;
- private static final int HARD_REMOVAL_VERSION = 37;
-
- private final SimpleStatement<Exception> mBaseStatement = new SimpleStatement<>();
-
- @Override
- protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
- session.spyStatic(Car.class);
- }
-
- @Test
- public void failWhenTestMethodIsMissingAnnotations() throws Throwable {
- Description testMethod = newTestMethod();
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*missing.*@ApiTest.*@NonApiTest.*@CddTest.*annotation.*");
- }
-
- @Test
- public void failWhenTestMethodHasNonApiTestAndCddTest() throws Throwable {
- Description testMethod = newTestMethod(
- new NonApiTestAnnotation(new ReasonType[]{}, NON_API_TEST_JUSTIFICATION),
- CddTestAnnotation.forRequirement("Boot in 1s"),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*both.*NonApiTest.*CddTest.*annotation.*");
- }
-
- @Test
- public void failWhenTestMethodHasApiTestAnnotationButItsNull() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation((String[]) null));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .contains("empty @ApiTest annotation");
- }
-
- @Test
- public void failWhenTestMethodHasApiTestAnnotationButItsEmpty() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation(new String[0]));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .contains("empty @ApiTest annotation");
- }
-
- @Test
- public void failWhenTestMethodHasApiTestAnnotationButItsInvalid() throws Throwable {
- String methodName = INVALID_API;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .contains(methodName);
- }
-
- @Test
- public void failWhenTestMethodHasInvalidApiTestAnnotatedWithIgnoreInvalidApiButWithoutApiRequirements()
- throws Throwable {
- String methodName = INVALID_API;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName),
- new IgnoreInvalidApiAnnotation("I validate, therefore am!"));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*contains @IgnoreInvalidApi.*missing.*@ApiRequirements.*");
- }
-
- @Test
- public void
- passWhenTestMethodHasInvalidApiTestButAnnotatedWithIgnoreInvalidApiAndApiRequirements()
- throws Throwable {
- String methodName = INVALID_API;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName),
- new IgnoreInvalidApiAnnotation("I validate, therefore am!"),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void failWhenTestMethodHasValidApiTestAnnotationButNoApiRequirements() throws Throwable {
- String methodName = VALID_API_WITHOUT_ANNOTATIONS;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .contains("@ApiRequirements");
- }
-
- @Test
- public void passWhenTestMethodHasValidApiTestAnnotationWithAddedInOrBefore() throws Throwable {
- String methodName = VALID_API_ADDED_IN_OR_BEFORE;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void failWhenTestMethodHasValidApiTestAnnotationAndApiRequirements() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .contains("both @ApiTest and @ApiRequirements");
- }
-
- @Test
- public void failWhenTestMethodHasCddTestAnnotationWithDeprecatedRequirement() throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirement("Boot in 1s"));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat().matches(
- ".*CddTest.*deprecated.*requirement.*Boot in 1s.*requirements.*instead.*");
- }
-
- @Test
- public void failWhenTestMethodHasCddTestAnnotationWithEmptyRequirements() throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements());
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*CddTest.*requirement.*empty.*");
- }
-
- @Test
- public void failWhenTestMethodHasCddTestAnnotationWithInvalidRequirements() throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements("", " ", null));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*CddTest.*empty.*requirement.*");
- }
-
- @Test
- public void failWhenTestMethodHasCddTestAnnotationButNoApiRequirements() throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements("Boot in 10m"));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*CddTest.*missing.*ApiRequirements.*");
- }
-
- @Test
- public void failWhenTestMethodHasNonApiTestAnnotationButNoApiRequirements() throws Throwable {
- Description testMethod = newTestMethod(
- new NonApiTestAnnotation(new ReasonType[]{}, NON_API_TEST_JUSTIFICATION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(new SimpleStatement<>(), testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
- .matches(".*NonApiTest.*missing.*ApiRequirements.*");
- }
-
- @Test
- public void passWhenTestMethodHasNonApiTestAnnotation() throws Throwable {
- Description testMethod = newTestMethod(
- new NonApiTestAnnotation(new ReasonType[]{}, NON_API_TEST_JUSTIFICATION),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodHasValidCddTestAnnotation() throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements("Boot in 10m"),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodHasValidApiTestAnnotation() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- // NOTE: ideally we should test it for versions < T as all (for ATS), but unfortunately that's
- // not possible because Build.VERSION.SDK_INT cannot be mocked
- @Test
- public void passWhenTestMethodIsMissingAnnotationsButPlatformIsNotSupported() throws Throwable {
- mockCarIsApiVersionAtLeast(Build.VERSION_CODES.TIRAMISU, /* minor= */ 1, /* isIt =*/ false);
- Description testMethod = newTestMethod();
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodIsMissingAnnotationsButItsNotEnforced() throws Throwable {
- Description testMethod = newTestMethod();
- ApiCheckerRule rule = new ApiCheckerRule.Builder().disableAnnotationsCheck().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodIsMissingApiRequirementsButItsNotEnforcedByDisableAnnotationsCheck() throws Throwable {
- String methodName = VALID_API_WITHOUT_ANNOTATIONS;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().disableAnnotationsCheck().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodIsMissingApiRequirementsButItsNotEnforcedByDisableApiRequirementsCheck() throws Throwable {
- String methodName = VALID_API_WITHOUT_ANNOTATIONS;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().disableApiRequirementsCheck().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void failWhenTestMethodRunsOnUnsupportedVersionsAndDoesntThrow() throws Throwable {
- String methodName = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- PlatformVersionMismatchExceptionNotThrownException e = assertThrows(
- PlatformVersionMismatchExceptionNotThrownException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_0);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- }
-
- @Test
- public void passWhenTestMethodRunsOnUnsupportedVersionsAndDoesntThrow() throws Throwable {
- String methodName = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- CarVersion carVersion = CarVersion.VERSION_CODES.TIRAMISU_1;
- PlatformVersion platformVersion = PlatformVersion.VERSION_CODES.TIRAMISU_0;
- mockCarGetCarVersion(carVersion);
- mockCarGetPlatformVersion(platformVersion);
- mBaseStatement.failWith(
- new PlatformVersionMismatchException(PlatformVersion.VERSION_CODES.TIRAMISU_1));
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void passWhenTestMethodContainsCompatibleApiRequirements() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0,
- ANOTHER_VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void failWhenTestMethodContainsIncompatibleApiRequirements() throws Throwable {
- Description testMethod = newTestMethod(new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0,
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IncompatibleApiRequirementsException e = assertThrows(
- IncompatibleApiRequirementsException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("apis on exception").that(e.getApis()).containsExactly(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0,
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1).inOrder();
-
- List<ApiRequirements> apiRequirements = e.getApiRequirements();
- assertWithMessage("apiRequirements on exception").that(apiRequirements).hasSize(2);
-
- ApiRequirements apiRequirements0 = apiRequirements.get(0);
- assertWithMessage("apiRequirements[0].minCarVersion on exception")
- .that(apiRequirements0.minCarVersion())
- .isEqualTo(ApiRequirements.CarVersion.TIRAMISU_0);
- assertWithMessage("apiRequirements[0].minPlatformVersion on exception")
- .that(apiRequirements0.minPlatformVersion())
- .isEqualTo(ApiRequirements.PlatformVersion.TIRAMISU_0);
-
- ApiRequirements apiRequirements1 = apiRequirements.get(1);
- assertWithMessage("apiRequirements[0].minCarVersion on exception")
- .that(apiRequirements1.minCarVersion())
- .isEqualTo(ApiRequirements.CarVersion.TIRAMISU_1);
- assertWithMessage("apiRequirements[0].minPlatformVersion on exception")
- .that(apiRequirements1.minPlatformVersion())
- .isEqualTo(ApiRequirements.PlatformVersion.TIRAMISU_1);
- }
-
- @Test
- public void failWhenUnsupportedVersionDoesNotHaveSupportedVersionTest() throws Throwable {
- Description testMethod = newTestMethod(new UnsupportedVersionTestAnnotation(""));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .contains("missing supportedVersionTest");
- }
-
- @Test
- public void failWhenUnsupportedVersionTestPointsToInvalidMethod() throws Throwable {
- Description testMethod = newTestMethod(
- new UnsupportedVersionTestAnnotation("supportedIAm"));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .containsMatch(".*invalid supportedVersionTest.*supportedIAm.*");
- }
-
- @Test
- public void failWhenUnsupportedVersionTestPointsToValidMethodWithoutSupportedVersionAnnotation()
- throws Throwable {
- Description testMethod = newTestMethod(new UnsupportedVersionTestAnnotation(
- METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .containsMatch(".*invalid supportedVersionTest.*"
- + METHOD_NAME_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0
- + ".*annotated with @SupportedVersionTest");
- }
-
- @Test
- public void
- failWhenUnsupportedVersionTestPointsToValidMethodWithInvalidSupportedVersionAnnotation()
- throws Throwable {
- Description testMethod = newTestMethod(new UnsupportedVersionTestAnnotation(
- METHOD_NAME_ANNOTATED_WITH_WRONG_SUPPORTED_VERSION_ANNOTATION));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .containsMatch(".*invalid unsupportedVersionTest.*"
- + METHOD_NAME_INVALID
- + ".*method "
- + METHOD_NAME_ANNOTATED_WITH_WRONG_SUPPORTED_VERSION_ANNOTATION
- + ".*should be.*" + TEST_METHOD_BEING_EXECUTED);
- }
-
- @Test
- public void failWhenTestHaveBothUnsupportedAndSupportedVersionTestAnnotations()
- throws Throwable {
- Description testMethod = newTestMethod(
- new UnsupportedVersionTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0),
- new SupportedVersionTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_0));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .contains("either supportedVersionTest or unsupportedVersionTest, not both");
- }
-
- @Test
- public void ignoreTestWithUnsupportedVersionTest_DEFAULT_OnSupportedVersion() throws Throwable {
- ignoreTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnSupportedVersion(
- /*behavior= */ null);
- }
-
- @Test
- public void ignoreTestWithUnsupportedVersionTest_EXPECT_THROWS_OnSupportedVersion()
- throws Throwable {
- ignoreTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnSupportedVersion(
- Behavior.EXPECT_THROWS_VERSION_MISMATCH_EXCEPTION);
- }
-
- private void ignoreTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnSupportedVersion(
- Behavior behavior) throws Throwable {
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1),
- new UnsupportedVersionTestAnnotation(behavior,
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- ExpectedVersionAssumptionViolationException e = assertThrows(
- ExpectedVersionAssumptionViolationException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- }
-
- @Test
- public void runTestWithUnsupportedVersionTest_DEFAULT_OnUnsupportedVersion()
- throws Throwable {
- runTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnUnsupportedVersion(
- /* behavior= */ null);
- }
-
- @Test
- public void runTestWithUnsupportedVersionTest_EXPECT_THROWS_OnUnsupportedVersion()
- throws Throwable {
- runTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnUnsupportedVersion(
- Behavior.EXPECT_THROWS_VERSION_MISMATCH_EXCEPTION);
- }
-
- private void runTestWithUnsupportedVersionTest_DEFAULT_or_EXPECT_THROWS_OnUnsupportedVersion(
- Behavior behavior) throws Throwable {
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1),
- new UnsupportedVersionTestAnnotation(behavior,
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- PlatformVersionMismatchExceptionNotThrownException e = assertThrows(
- PlatformVersionMismatchExceptionNotThrownException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_0);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- }
-
- @Test
- public void runTestWithUnsupportedVersionTest_EXPECT_PASS_OnSupportedVersion()
- throws Throwable {
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1),
- new UnsupportedVersionTestAnnotation(Behavior.EXPECT_PASS,
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- ExpectedVersionAssumptionViolationException e = assertThrows(
- ExpectedVersionAssumptionViolationException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- }
-
- @Test
- public void runTestWithUnsupportedVersionTest_EXPECT_PASS_OnUnsupportedVersion()
- throws Throwable {
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(
- VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1),
- new UnsupportedVersionTestAnnotation(Behavior.EXPECT_PASS,
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void failTestWithSupportedVersionMissingUnsupported() throws Throwable {
- Description testMethod = newTestMethod(new SupportedVersionTestAnnotation(""));
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .contains("missing unsupportedVersionTest");
- }
-
- @Test
- public void failTestWithSupportedVersionWithWrongUnsupported() throws Throwable {
- String methodName = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- Description testMethod = newTestMethod(new ApiTestAnnotation(methodName),
- new SupportedVersionTestAnnotation(
- METHOD_NAME_ANNOTATED_WITH_WRONG_UNSUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.message").that(e).hasMessageThat()
- .containsMatch(".*invalid supportedVersionTest.*"
- + "method.*"
- + METHOD_NAME_ANNOTATED_WITH_WRONG_UNSUPPORTED_VERSION_ANNOTATION
- + ".*should be.*" + TEST_METHOD_BEING_EXECUTED);
- }
-
- @Test
- public void runTestWithSupportedVersionTestOnSupportedVersion() throws Throwable {
- String methodName = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(methodName),
- new SupportedVersionTestAnnotation(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void ignoreTestWithSupportedVersionTestOnUnsupportedVersion() throws Throwable {
- String methodName = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- Description testMethod = newTestMethod(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION,
- new ApiTestAnnotation(methodName),
- new SupportedVersionTestAnnotation(
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION));
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- ExpectedVersionAssumptionViolationException e = assertThrows(
- ExpectedVersionAssumptionViolationException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_0);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- }
-
- // NOTE: technically, we should have tests for CddTest-based annotation for all scenarios above
- // (like using @UnsupportedVersionTest / @SupportedVersionTest annotations), but pragmatically
- // speaking, just 2 tests for the unsupported version are enough)
-
- @Test
- public void failWhenTestMethodWithCddTestRunsOnUnsupportedVersionsAndDoesntThrow()
- throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements("Boot in 10m"),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- PlatformVersionMismatchExceptionNotThrownException e = assertThrows(
- PlatformVersionMismatchExceptionNotThrownException.class,
- () -> rule.apply(mBaseStatement, testMethod).evaluate());
- Log.d(TAG, "Exception: " + e);
-
- assertWithMessage("exception.carVersion").that(e.getCarVersion())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.platformVersion").that(e.getPlatformVersion())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_0);
- ApiRequirements apiRequirements = e.getApiRequirements();
- assertWithMessage("exception.apiRequirements.minCarVersion")
- .that(apiRequirements.minCarVersion().get())
- .isEqualTo(CarVersion.VERSION_CODES.TIRAMISU_1);
- assertWithMessage("exception.apiRequirements.minPlatformVersion")
- .that(apiRequirements.minPlatformVersion().get())
- .isEqualTo(PlatformVersion.VERSION_CODES.TIRAMISU_1);
- }
-
- @Test
- public void passWhenTestMethodWithCddTestRunsOnUnsupportedVersionsAndDoesntThrow()
- throws Throwable {
- Description testMethod = newTestMethod(CddTestAnnotation.forRequirements("Boot in 10m"),
- new ApiRequirementsAnnotation(ApiRequirements.CarVersion.TIRAMISU_1,
- ApiRequirements.PlatformVersion.TIRAMISU_1, SOFT_REMOVAL_VERSION,
- HARD_REMOVAL_VERSION));
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- CarVersion carVersion = CarVersion.VERSION_CODES.TIRAMISU_1;
- PlatformVersion platformVersion = PlatformVersion.VERSION_CODES.TIRAMISU_0;
- mockCarGetCarVersion(carVersion);
- mockCarGetPlatformVersion(platformVersion);
- mBaseStatement.failWith(
- new PlatformVersionMismatchException(PlatformVersion.VERSION_CODES.TIRAMISU_1));
-
- rule.apply(mBaseStatement, testMethod).evaluate();
-
- mBaseStatement.assertEvaluated();
- }
-
- @Test
- public void testIsApiSupported_null() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- assertThrows(NullPointerException.class, () -> rule.isApiSupported(null));
- }
-
- @Test
- public void testIsApiSupported_invalidApi() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- assertThrows(IllegalArgumentException.class, ()-> rule.isApiSupported(INVALID_API));
- }
-
- @Test
- public void testIsApiSupported_validApiButWithoutApiRequirements() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
-
- assertThrows(IllegalArgumentException.class,
- () -> rule.isApiSupported(VALID_API_WITHOUT_ANNOTATIONS));
- }
-
- @Test
- public void testIsApiSupported_carVersionNotSupported() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- String api = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_0);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- assertWithMessage("isApiSupported(%s) when CarVersion is not supported", api)
- .that(rule.isApiSupported(api)).isTrue();
- }
-
- @Test
- public void testIsApiSupported_platformVersionNotSupported() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- String api = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
-
- assertWithMessage("isApiSupported(%s) when PlatformVersion is not supported", api)
- .that(rule.isApiSupported(api)).isFalse();
- }
-
- @Test
- public void testIsApiSupported_supported() throws Throwable {
- ApiCheckerRule rule = new ApiCheckerRule.Builder().build();
- String api = VALID_API_THAT_REQUIRES_CAR_AND_PLATFORM_TIRAMISU_1;
- mockCarGetCarVersion(CarVersion.VERSION_CODES.TIRAMISU_1);
- mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
-
- assertWithMessage("isApiSupported(%s) when CarVersion and PlatformVersion are supported",
- api).that(rule.isApiSupported(api)).isTrue();
- }
-
- @Test
- public void testGetTestName() throws Throwable {
- Description testMethod = newTestMethod();
- ApiCheckerRule rule = new ApiCheckerRule.Builder().disableAnnotationsCheck().build();
- expectWithMessage("rule.getTestName() before test").that(rule.getTestMethodName()).isNull();
-
- // Need to save the name while the Statements is being executed
- StringBuilder testNameDuringTest = new StringBuilder();
- Statement statement = mock(Statement.class);
- doAnswer((i) -> {
- testNameDuringTest.append(rule.getTestMethodName());
- return null;
- }).when(statement).evaluate();
-
- rule.apply(statement, testMethod).evaluate();
-
- expectWithMessage("rule.getTestName() during test").that(testNameDuringTest.toString())
- .isEqualTo(TEST_METHOD_BEING_EXECUTED);
- expectWithMessage("rule.getTestName() after test").that(rule.getTestMethodName()).isNull();
- }
-
- ////////////////////////////////////
- // Start of members used on tests //
- ////////////////////////////////////
-
- @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_0,
- minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
- public void requiresCarAndPlatformTiramisu0() {
-
- }
-
- @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1,
- minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_1)
- public void requiresCarAndPlatformTiramisu1() {
-
- }
-
- @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_0,
- minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
- public void alsoRequiresCarAndPlatformTiramisu0() {
-
- }
-
- public void apiYUNoAnnotated() {
-
- }
-
- @SupportedVersionTest(unsupportedVersionTest = METHOD_NAME_INVALID)
- public void annotatedWithSupportedVersionAnnotationWithWrongUnsupportedField() {
- }
- @SupportedVersionTest(unsupportedVersionTest =
- METHOD_NAME_ANNOTATED_WITH_RIGHT_UNSUPPORTED_VERSION_ANNOTATION)
- public void annotatedWithSupportedVersionAnnotationWithRightUnsupportedField() {
- }
-
- @UnsupportedVersionTest(supportedVersionTest = METHOD_NAME_INVALID)
- public void annotatedWithUnsupportedVersionAnnotationWithWrongSupportedField() {
- }
-
- @UnsupportedVersionTest(supportedVersionTest =
- METHOD_NAME_ANNOTATED_WITH_RIGHT_SUPPORTED_VERSION_ANNOTATION)
- public void annotatedWithUnsupportedVersionAnnotationWithRightSupportedField() {
- }
-
- @AddedInOrBefore(majorVersion = 42)
- public void annotatedWithAddedInOrBefore() {
-
- }
-
- ////////////////////////////////////
- // End of members used on tests //
- ////////////////////////////////////
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class UnsupportedVersionTestAnnotation implements UnsupportedVersionTest {
- private final Behavior mBehavior;
- private final String mSupportedVersionTest;
-
- UnsupportedVersionTestAnnotation(Behavior behavior, String supportedVersionTest) {
- mBehavior = behavior;
- mSupportedVersionTest = supportedVersionTest;
- }
-
- UnsupportedVersionTestAnnotation(String supportedVersionTest) {
- this(/* behavior= */ null, supportedVersionTest);
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return ApiCheckerRule.UnsupportedVersionTest.class;
- }
-
- @Override
- public String supportedVersionTest() {
- return mSupportedVersionTest;
- }
-
- @Override
- public Behavior behavior() {
- return mBehavior;
- }
-
- @Override
- public String toString() {
- return "@UnsupportedVersionTestAnnotation(supportedVersionTest="
- + mSupportedVersionTest + ", behavior=" + mBehavior + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class SupportedVersionTestAnnotation implements SupportedVersionTest {
- private final String mUnsupportedVersionTest;
-
- SupportedVersionTestAnnotation(String unsupportedVersionTest) {
- mUnsupportedVersionTest = unsupportedVersionTest;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return ApiCheckerRule.SupportedVersionTest.class;
- }
-
- @Override
- public String unsupportedVersionTest() {
- return mUnsupportedVersionTest;
- }
-
- @Override
- public String toString() {
- return "@SupportedVersionTestAnnotation(unsupportedVersionTest="
- + mUnsupportedVersionTest + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class ApiTestAnnotation implements ApiTest {
-
- private final String[] mApis;
-
- ApiTestAnnotation(String... apis) {
- mApis = apis;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return ApiTest.class;
- }
-
- @Override
- public String[] apis() {
- return mApis;
- }
-
- @Override
- public String toString() {
- return "@ApiTestAnnotation(" + Arrays.toString(mApis) + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class ApiRequirementsAnnotation implements ApiRequirements {
-
- private final CarVersion mCarVersion;
- private final PlatformVersion mPlatformVersion;
- private final int mSoftRemovalVersion;
- private final int mHardRemovalVersion;
-
- ApiRequirementsAnnotation(CarVersion carVersion, PlatformVersion platformVersion,
- int softRemovalVersion, int hardRemovalVersion) {
- mCarVersion = Objects.requireNonNull(carVersion);
- mPlatformVersion = Objects.requireNonNull(platformVersion);
- mSoftRemovalVersion = softRemovalVersion;
- mHardRemovalVersion = hardRemovalVersion;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return ApiRequirements.class;
- }
-
- @Override
- public CarVersion minCarVersion() {
- return mCarVersion;
- }
-
- @Override
- public PlatformVersion minPlatformVersion() {
- return mPlatformVersion;
- }
-
- @Override
- public int softRemovalVersion() { return mSoftRemovalVersion; }
-
- @Override
- public int hardRemovalVersion() { return mHardRemovalVersion; }
-
- @Override
- public String toString() {
- return "@ApiRequirementsAnnotation(carVersion=" + mCarVersion + ", platformVersion="
- + mPlatformVersion + ", softRemovalVersion=" + mSoftRemovalVersion
- + ", hardRemovalVersion= " + mHardRemovalVersion + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class CddTestAnnotation implements CddTest {
-
- private final String mRequirement;
- private final String[] mRequirements;
-
- static CddTestAnnotation forRequirement(String requirement) {
- return new CddTestAnnotation(requirement, /* requirements= */ (String[]) null);
-
- }
-
- static CddTestAnnotation forRequirements(String... requirements) {
- return new CddTestAnnotation(/* requirement= */ null, requirements);
-
- }
-
- private CddTestAnnotation(String requirement, String[] requirements) {
- mRequirement = requirement;
- mRequirements = requirements;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return CddTest.class;
- }
-
- @Override
- public String requirement() {
- return mRequirement;
- }
-
- @Override
- public String[] requirements() {
- return mRequirements;
- }
-
- @Override
- public String toString() {
- return "@CddTestAnnotation(requirement='" + mRequirement + "', requirements="
- + Arrays.toString(mRequirements) + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation")// We don't care about equals() / hashCode()
- private static final class NonApiTestAnnotation implements NonApiTest {
-
- private final ReasonType[] mExemptionReasons;
- private final String mJustification;
-
- NonApiTestAnnotation(ReasonType[] exemptionReasons, String justification) {
- mExemptionReasons = exemptionReasons;
- mJustification = justification;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return NonApiTest.class;
- }
-
- @Override
- public ReasonType[] exemptionReasons() {
- return mExemptionReasons;
- }
-
- @Override
- public String justification() {
- return mJustification;
- }
-
- @Override
- public String toString() {
- return "@NonApiTest(exemptionReasons=" + Arrays.toString(mExemptionReasons)
- + ", justification=" + mJustification + ")";
- }
- }
-
- @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
- private static final class IgnoreInvalidApiAnnotation implements IgnoreInvalidApi {
-
- private final String mReason;
-
- IgnoreInvalidApiAnnotation(String reason) {
- mReason = reason;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return IgnoreInvalidApi.class;
- }
-
- @Override
- public String reason() {
- return mReason;
- }
-
- @Override
- public String toString() {
- return "@IgnoreInvalidApiAnnotation(reason='" + mReason + ")";
- }
- }
-}
diff --git a/tests/carservice_unit_test/src/android/car/test/JUnitHelper.java b/tests/carservice_unit_test/src/android/car/test/JUnitHelper.java
index 4aa8f4c..45aa9ba 100644
--- a/tests/carservice_unit_test/src/android/car/test/JUnitHelper.java
+++ b/tests/carservice_unit_test/src/android/car/test/JUnitHelper.java
@@ -40,7 +40,7 @@
}
public static Description newTestMethod(String methodName, Annotation... annotations) {
- return Description.createTestDescription(ApiCheckerRuleTest.class,
+ return Description.createTestDescription(PermissionsCheckerRuleTest.class,
methodName, annotations);
}
diff --git a/tests/carservice_unit_test/src/com/android/car/AidlVehicleStubUnitTest.java b/tests/carservice_unit_test/src/com/android/car/AidlVehicleStubUnitTest.java
index 55c6c8d..67a4009 100644
--- a/tests/carservice_unit_test/src/com/android/car/AidlVehicleStubUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/AidlVehicleStubUnitTest.java
@@ -784,7 +784,9 @@
HalPropValue value = HVAC_PROP_VALUE;
- long timeoutUptimeMs = SystemClock.uptimeMillis() + 10;
+ // Requests will timeout after 100ms from this point. Don't make this too short otherwise
+ // the okay result will timeout.
+ long timeoutUptimeMs = SystemClock.uptimeMillis() + 100;
AsyncGetSetRequest getVehicleStubAsyncRequest1 = new AsyncGetSetRequest(
/* serviceRequestId= */ 0, value, timeoutUptimeMs);
AsyncGetSetRequest getVehicleStubAsyncRequest2 = new AsyncGetSetRequest(
diff --git a/tests/carservice_unit_test/src/com/android/car/CarInputRotaryServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarInputRotaryServiceTest.java
index 992af95..672287f 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarInputRotaryServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarInputRotaryServiceTest.java
@@ -297,7 +297,7 @@
UserHalService userHal = mock(UserHalService.class);
mCarUserService = new CarUserService(mMockContext, userHal,
userManager, /* maxRunningUsers= */ 2,
- mUxRestrictionService, mCarPackageManagerService);
+ mUxRestrictionService, mCarPackageManagerService, mCarOccupantZoneService);
mCarInputService = new CarInputService(mMockContext, mInputHalService, mCarUserService,
mCarOccupantZoneService, mCarBluetoothService, mCarPowerManagementService,
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPropertyManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/CarPropertyManagerUnitTest.java
index 6bd45de..31dfd8d 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPropertyManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPropertyManagerUnitTest.java
@@ -360,6 +360,81 @@
}
@Test
+ public void testGetProperty_returnsValueWithUnavailableStatusBeforeU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
+ new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
+ CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f));
+ }
+
+ @Test
+ public void testGetProperty_valueWithUnavailableStatusThrowsAfterU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThrows(PropertyNotAvailableException.class,
+ () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
+ }
+
+ @Test
+ public void testGetProperty_returnsValueWithErrorStatusBeforeU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
+ new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f));
+ }
+
+ @Test
+ public void testGetProperty_valueWithErrorStatusThrowsAfterU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThrows(CarInternalErrorException.class,
+ () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
+ }
+
+ @Test
+ public void testGetProperty_returnsValueWithUnknownStatusBeforeU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
+ new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0, /*status=*/999,
+ TEST_TIMESTAMP, 17.0f));
+ }
+
+ @Test
+ public void testGetProperty_valueWithUnknownStatusThrowsAfterU() throws RemoteException {
+ setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
+
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThrows(CarInternalErrorException.class,
+ () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
+ }
+
+ @Test
public void testGetPropertyWithClass() throws Exception {
CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
@@ -1911,6 +1986,16 @@
}
@Test
+ public void testIsPropertyAvailable_withValueWithNotAvailableStatus() throws Exception {
+ CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
+ CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
+ when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
+
+ assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
+ .isFalse();
+ }
+
+ @Test
public void testIsPropertyAvailable() throws Exception {
CarPropertyValue<Integer> expectedValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET,
/* areaId= */ 0, CarPropertyValue.STATUS_AVAILABLE, /* timestamp= */ 0,
diff --git a/tests/carservice_unit_test/src/com/android/car/CarServiceUtilsTest.java b/tests/carservice_unit_test/src/com/android/car/CarServiceUtilsTest.java
index 03fd536..5980e07 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarServiceUtilsTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarServiceUtilsTest.java
@@ -26,6 +26,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -124,7 +125,6 @@
@Test
public void testStartSystemUiForUser() {
int userId = NON_CURRENT_USER_ID;
- mockContextCreateContextAsUser(mMockContext, mMockUserContext, userId);
Resources resources = mock(Resources.class);
String systemUiComponent = "test.systemui/test.systemui.TestSystemUIService";
when(resources.getString(com.android.internal.R.string.config_systemUIServiceComponent))
@@ -134,7 +134,7 @@
CarServiceUtils.startSystemUiForUser(mMockContext, userId);
- verify(mMockUserContext).startService(intentCaptor.capture());
+ verify(mMockContext).bindServiceAsUser(intentCaptor.capture(), any(), anyInt(), any());
assertThat(intentCaptor.getValue().getComponent()).isEqualTo(
ComponentName.unflattenFromString(systemUiComponent));
}
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/CarDevicePolicyServiceTest.java b/tests/carservice_unit_test/src/com/android/car/admin/CarDevicePolicyServiceTest.java
index 76ed9b5..0d84503 100644
--- a/tests/carservice_unit_test/src/com/android/car/admin/CarDevicePolicyServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/admin/CarDevicePolicyServiceTest.java
@@ -39,6 +39,7 @@
import android.car.SyncResultCallback;
import android.car.admin.CarDevicePolicyManager;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.user.UserCreationRequest;
import android.car.user.UserCreationResult;
import android.car.user.UserRemovalResult;
import android.car.user.UserStartResult;
@@ -52,6 +53,7 @@
import android.content.pm.UserInfo.UserInfoFlag;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import com.android.car.BuiltinPackageDependency;
import com.android.car.CarServiceUtils;
@@ -64,6 +66,7 @@
import org.mockito.Mock;
public final class CarDevicePolicyServiceTest extends AbstractExtendedMockitoTestCase {
+ private static final String TAG = CarDevicePolicyServiceTest.class.getSimpleName();
@Mock
private CarUserService mCarUserService;
@@ -170,10 +173,31 @@
private void createUserOkTest(@UserInfoFlag int flags,
@CarDevicePolicyManager.UserType int carDpmUserType, @NonNull String userType) {
mService.createUser("name", carDpmUserType, mUserCreationResultCallbackImpl);
+ UserCreationRequest.Builder userCreationRequestBuilder =
+ new UserCreationRequest.Builder().setName("name");
+ switch(carDpmUserType) {
+ case CarDevicePolicyManager.USER_TYPE_REGULAR:
+ break;
+ case CarDevicePolicyManager.USER_TYPE_ADMIN:
+ userCreationRequestBuilder.setAdmin();
+ break;
+ case CarDevicePolicyManager.USER_TYPE_GUEST:
+ userCreationRequestBuilder.setGuest();
+ break;
+ default:
+ Log.d(TAG, "CarUserService.createUser(): invalid carDpmUserType (" + carDpmUserType
+ + ")");
+ break;
+ }
+ UserCreationRequest userCreationRequest = userCreationRequestBuilder.build();
- // TODO(b/278124479): Match based on name, userType, flags.
- verify(mCarUserService).createUser(/* userCreationRequest= */ any(), /* timeoutMs= */
+ ArgumentCaptor<UserCreationRequest> argument = ArgumentCaptor.forClass(
+ UserCreationRequest.class);
+ verify(mCarUserService).createUser(argument.capture(), /* timeoutMs= */
anyInt(), eq(mUserCreationResultCallbackImpl));
+ assertWithMessage("UserCreationRequest").that(argument.getValue().toString()).isEqualTo(
+ userCreationRequest.toString());
+
}
@Test
diff --git a/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceTaskMonitorUnitTest.java b/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceTaskMonitorUnitTest.java
index 0682e0a..3287996 100644
--- a/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceTaskMonitorUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceTaskMonitorUnitTest.java
@@ -209,7 +209,7 @@
@Test
public void testActivityBlocking() throws Exception {
- Intent blockingIntent = new Intent();
+ Intent blockingIntent = new Intent().setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
blockingIntent.setComponent(mBlockingActivity);
// start a black listed activity
diff --git a/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java b/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
index e2b03f8..3c0530d 100644
--- a/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
@@ -68,6 +68,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.stubbing.OngoingStubbing;
import java.util.ArrayList;
@@ -117,6 +118,7 @@
doReturn(mCarUserService).when(() -> CarLocalServices.getService(CarUserService.class));
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mContext));
when(mDisplayManager.getDisplay(VALID_DISPLAY_ID)).thenReturn(mValidDisplay);
+ when(mValidDisplay.getState()).thenReturn(Display.STATE_ON);
mFixedActivityService = new FixedActivityService(mContext,
mActivityService, mDisplayManager, mUserHandleHelper);
}
@@ -369,9 +371,10 @@
}
@Test
- public void testStartFixedActivityModeForDisplayAndUser_invalidDisplay() {
+ public void testStartFixedActivityModeForDisplayAndUser_invalidDisplay() throws Exception {
int userId = 100;
- Intent intent = new Intent(Intent.ACTION_MAIN);
+ mockAmGetCurrentUser(userId);
+ Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(VALID_DISPLAY_ID);
int invalidDisplayId = Display.DEFAULT_DISPLAY;
@@ -381,9 +384,10 @@
}
@Test
- public void testStartFixedActivityModeForDisplayAndUser_unavailableDisplay() {
+ public void testStartFixedActivityModeForDisplayAndUser_unavailableDisplay() throws Exception {
int userId = 100;
- Intent intent = new Intent(Intent.ACTION_MAIN);
+ mockAmGetCurrentUser(userId);
+ Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(VALID_DISPLAY_ID);
int unavailableDisplayId = VALID_DISPLAY_ID + 1;
@@ -393,6 +397,44 @@
}
@Test
+ public void testStartFixedActivityModeForDisplayAndUser_nonActiveDisplay() throws Exception {
+ int mockDisplayId = VALID_DISPLAY_ID + 1;
+ Display mockDisplay = Mockito.mock(Display.class);
+ when(mDisplayManager.getDisplay(mockDisplayId)).thenReturn(mockDisplay);
+ when(mockDisplay.getState()).thenReturn(Display.STATE_OFF, Display.STATE_ON);
+
+ int userId = 100;
+ mockAmGetCurrentUser(userId);
+ Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
+ ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(mockDisplayId);
+
+ // Display.STATE_OFF
+ boolean started1 = mFixedActivityService.startFixedActivityModeForDisplayAndUser(
+ intent, options, mockDisplayId, userId);
+ assertThat(started1).isFalse();
+
+ verify(mContext, never()).startActivityAsUser(any(Intent.class), any(Bundle.class),
+ any(UserHandle.class));
+
+ // Display.STATE_ON
+ boolean started2 = mFixedActivityService.startFixedActivityModeForDisplayAndUser(
+ intent, options, mockDisplayId, userId);
+ assertThat(started2).isTrue();
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<Bundle> activityOptionsCaptor = ArgumentCaptor.forClass(Bundle.class);
+ ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(UserHandle.class);
+
+ verify(mContext, times(1)).startActivityAsUser(intentCaptor.capture(),
+ activityOptionsCaptor.capture(), userHandleCaptor.capture());
+ assertThat(userHandleCaptor.getAllValues().get(0)).isEqualTo(UserHandle.of(userId));
+ assertThat(ActivityOptions.fromBundle(activityOptionsCaptor.getAllValues().get(0))
+ .getLaunchDisplayId()).isEqualTo(mockDisplayId);
+ Intent capturedIntent = intentCaptor.getAllValues().get(0);
+ assertThat(capturedIntent.getComponent()).isEqualTo(intent.getComponent());
+ }
+
+ @Test
public void testStartFixedActivityModeForDisplayAndUser_displayRemoved()
throws Exception {
int displayToBeRemoved = VALID_DISPLAY_ID + 1;
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioContextInfoTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioContextInfoTest.java
index 32b1cb6..3d30a4a 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioContextInfoTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioContextInfoTest.java
@@ -35,9 +35,20 @@
public static final int TEST_CONTEXT_ID_100 = 100;
public static final int TEST_CONTEXT_ID_1000 = 1000;
public static final String TEST_CONTEXT_NAME_MUSIC = "music";
- public static final AudioAttributes TEST_AUDIO_ATTRIBUTE = CarAudioContext
+ public static final AudioAttributes TEST_MEDIA_AUDIO_ATTRIBUTE = CarAudioContext
.getAudioAttributeFromUsage(AudioAttributes.USAGE_MEDIA);
- public static final AudioAttributes[] TEST_AUDIO_ATTRIBUTES_ARRAY = {TEST_AUDIO_ATTRIBUTE};
+
+ public static final AudioAttributes TEST_NAV_AUDIO_ATTRIBUTE = CarAudioContext
+ .getAudioAttributeFromUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
+
+ public static final AudioAttributes TEST_NOTIFICATION_AUDIO_ATTRIBUTE = CarAudioContext
+ .getAudioAttributeFromUsage(AudioAttributes.USAGE_NOTIFICATION);
+ public static final AudioAttributes[] TEST_AUDIO_ATTRIBUTES_ARRAY =
+ {TEST_MEDIA_AUDIO_ATTRIBUTE};
+
+ public static final AudioAttributes[] TEST_ALL_AUDIO_ATTRIBUTES_ARRAY =
+ {TEST_MEDIA_AUDIO_ATTRIBUTE, TEST_NOTIFICATION_AUDIO_ATTRIBUTE,
+ TEST_NAV_AUDIO_ATTRIBUTE};
@Test
public void constructor_withNullAttributes_fails() {
@@ -118,21 +129,96 @@
TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_MIN_VALUE);
assertWithMessage("Car audio context info audio attributes")
- .that(info.getAudioAttributes()).asList().containsExactly(TEST_AUDIO_ATTRIBUTE);
+ .that(info.getAudioAttributes()).asList()
+ .containsExactly(TEST_MEDIA_AUDIO_ATTRIBUTE);
}
@Test
public void toString_withValidParameters() {
- String contextName = "music_audio";
- CarAudioContextInfo info = new CarAudioContextInfo(TEST_AUDIO_ATTRIBUTES_ARRAY, contextName,
- TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
assertWithMessage("Car audio context info string with context name")
- .that(info.toString()).contains(contextName);
+ .that(info.toString()).contains(TEST_CONTEXT_NAME_MUSIC);
assertWithMessage("Car audio context info string with context id")
.that(info.toString()).contains(Integer.toString(TEST_CONTEXT_ID_100));
assertWithMessage("Car audio context info string with audio attribute")
.that(info.toString()).contains(AudioAttributes
.usageToString(AudioAttributes.USAGE_MEDIA));
}
+
+ @Test
+ public void equals_forSameInfo() {
+ CarAudioContextInfo info1 = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+
+ assertWithMessage("Car audio context info equality").that(info.equals(info1)).isTrue();
+ }
+
+ @Test
+ public void equals_forSameObject() {
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info2 = info;
+
+ assertWithMessage("Car audio context info equality for same object")
+ .that(info).isEqualTo(info2);
+ }
+
+ @Test
+ public void equals_forNull() {
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+
+ assertWithMessage("Car audio context info equality for null object")
+ .that(info.equals(null)).isFalse();
+ }
+
+ @Test
+ public void equals_forDifferentId() {
+ CarAudioContextInfo info1 = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_1000);
+
+ assertWithMessage("Car audio context info equality for different id")
+ .that(info.equals(info1)).isFalse();
+ }
+
+ @Test
+ public void equals_forDifferentName() {
+ CarAudioContextInfo info1 = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ String contextName2 = "nav_audio";
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ contextName2, TEST_CONTEXT_ID_100);
+
+ assertWithMessage("Car audio context info equality for different name")
+ .that(info.equals(info1)).isFalse();
+ }
+
+ @Test
+ public void equals_forDifferentAttributes() {
+ CarAudioContextInfo info1 = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info = new CarAudioContextInfo(
+ new AudioAttributes[] {TEST_NAV_AUDIO_ATTRIBUTE, TEST_NAV_AUDIO_ATTRIBUTE},
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+
+ assertWithMessage("Car audio context info equality for different attributes")
+ .that(info.equals(info1)).isFalse();
+ }
+
+ @Test
+ public void hashCode_forSameInfo() {
+ CarAudioContextInfo info1 = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+ CarAudioContextInfo info = new CarAudioContextInfo(TEST_ALL_AUDIO_ATTRIBUTES_ARRAY,
+ TEST_CONTEXT_NAME_MUSIC, TEST_CONTEXT_ID_100);
+
+ assertWithMessage("Car audio context info hash code for same info")
+ .that(info.hashCode()).isEqualTo(info1.hashCode());
+ }
}
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioFocusUnitTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioFocusUnitTest.java
index 0d562cc..7f267a2 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioFocusUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioFocusUnitTest.java
@@ -16,6 +16,7 @@
package com.android.car.audio;
import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
+import static android.car.media.CarAudioManager.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS;
import static android.media.AudioAttributes.USAGE_ANNOUNCEMENT;
import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
import static android.media.AudioAttributes.USAGE_ASSISTANT;
@@ -26,10 +27,12 @@
import static android.media.AudioAttributes.USAGE_VEHICLE_STATUS;
import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
import static android.media.AudioManager.AUDIOFOCUS_GAIN;
+import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT;
import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
import static android.media.AudioManager.AUDIOFOCUS_LOSS;
import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
+import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_FAILED;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -58,6 +61,7 @@
import android.media.AudioManager;
import android.media.audiopolicy.AudioPolicy;
import android.os.Build;
+import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -966,6 +970,89 @@
}
@Test
+ public void onAudioFocus_transientDuckablyLoss() {
+ CarAudioFocus carAudioFocus = getCarAudioFocus();
+ AudioFocusInfo musicReceivingDuckEvent = getInfoThatReceivesDuckingEvents(USAGE_MEDIA,
+ FIRST_CLIENT_ID, AUDIOFOCUS_GAIN, /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo navFocusInfo = getInfo(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ SECOND_CLIENT_ID, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ /* acceptsDelayedFocus= */ false);
+ carAudioFocus.onAudioFocusRequest(musicReceivingDuckEvent, AUDIOFOCUS_REQUEST_GRANTED);
+
+ // Music loses focus transiently duckably
+ carAudioFocus.onAudioFocusRequest(navFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+
+ verify(mMockAudioManager).dispatchAudioFocusChange(musicReceivingDuckEvent,
+ AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK, mAudioPolicy);
+ }
+
+ @Test
+ public void onAudioFocus_transientDuckablyLoss_thenRegained() {
+ CarAudioFocus carAudioFocus = getCarAudioFocus();
+ AudioFocusInfo musicReceivingDuckEvent = getInfoThatReceivesDuckingEvents(USAGE_MEDIA,
+ FIRST_CLIENT_ID, AUDIOFOCUS_GAIN, /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo navFocusInfo = getInfo(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ SECOND_CLIENT_ID, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ /* acceptsDelayedFocus= */ false);
+ carAudioFocus.onAudioFocusRequest(musicReceivingDuckEvent,
+ AUDIOFOCUS_REQUEST_GRANTED);
+ carAudioFocus.onAudioFocusRequest(navFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+
+ // End of focus requested that leaded to duck music
+ carAudioFocus.onAudioFocusAbandon(navFocusInfo);
+
+ verify(mMockAudioManager).dispatchAudioFocusChange(musicReceivingDuckEvent,
+ AUDIOFOCUS_GAIN, mAudioPolicy);
+ }
+
+ @Test
+ public void onAudioFocus_transientDuckablyLoss_thenRegained_andLossTransient() {
+ CarAudioFocus carAudioFocus = getCarAudioFocus();
+ AudioFocusInfo musicReceivingDuckEvent = getInfoThatReceivesDuckingEvents(USAGE_MEDIA,
+ FIRST_CLIENT_ID, AUDIOFOCUS_GAIN, /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo navFocusInfo = getInfo(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ SECOND_CLIENT_ID, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo callRingFocusInfo = getInfo(USAGE_NOTIFICATION_RINGTONE, CALL_CLIENT_ID,
+ AUDIOFOCUS_GAIN_TRANSIENT, /* acceptsDelayedFocus= */ false);
+ carAudioFocus.onAudioFocusRequest(musicReceivingDuckEvent,
+ AUDIOFOCUS_REQUEST_GRANTED);
+ carAudioFocus.onAudioFocusRequest(navFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+ carAudioFocus.onAudioFocusAbandon(navFocusInfo);
+
+ // Ring requests focus, leading to loss transient
+ carAudioFocus.onAudioFocusRequest(callRingFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+
+ verify(mMockAudioManager).dispatchAudioFocusChange(musicReceivingDuckEvent,
+ AUDIOFOCUS_LOSS_TRANSIENT, mAudioPolicy);
+ }
+
+ @Test
+ public void onAudioFocus_transientDuckablyLoss_thenRegainedAndLossTransientByTwoBlockers() {
+ CarAudioFocus carAudioFocus = getCarAudioFocus();
+ AudioFocusInfo musicReceivingDuckEvent = getInfoThatReceivesDuckingEvents(USAGE_MEDIA,
+ FIRST_CLIENT_ID, AUDIOFOCUS_GAIN, /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo navFocusInfo = getInfo(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ SECOND_CLIENT_ID, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, false);
+ AudioFocusInfo callRingFocusInfo = getInfo(USAGE_NOTIFICATION_RINGTONE, CALL_CLIENT_ID,
+ AUDIOFOCUS_GAIN_TRANSIENT, /* acceptsDelayedFocus= */ false);
+ AudioFocusInfo callFocusInfo = getInfo(USAGE_VOICE_COMMUNICATION, CALL_CLIENT_ID,
+ AUDIOFOCUS_GAIN_TRANSIENT, /* acceptsDelayedFocus= */ false);
+ carAudioFocus.onAudioFocusRequest(musicReceivingDuckEvent,
+ AUDIOFOCUS_REQUEST_GRANTED);
+ carAudioFocus.onAudioFocusRequest(navFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+ carAudioFocus.onAudioFocusAbandon(navFocusInfo);
+ carAudioFocus.onAudioFocusRequest(callRingFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+
+ // Ring is replaced by call, adding another blocker
+ carAudioFocus.onAudioFocusRequest(callFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
+
+ // Music has already lost focus transiently, no spurious loss transient expected
+ verify(mMockAudioManager).dispatchAudioFocusChange(musicReceivingDuckEvent,
+ AUDIOFOCUS_LOSS_TRANSIENT, mAudioPolicy);
+ }
+
+ @Test
public void getAudioFocusHolders_withNoFocusHolders_returnsEmptyList() {
CarAudioFocus carAudioFocus = getCarAudioFocus();
@@ -1493,6 +1580,15 @@
flags, Build.VERSION.SDK_INT);
}
+ private AudioFocusInfo getInfoThatReceivesDuckingEvents(@AttributeUsage int usage,
+ String clientId, int gainType, boolean acceptsDelayedFocus) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS, true);
+ AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(usage)
+ .addBundle(bundle).build();
+ return getInfo(audioAttributes, clientId, gainType, acceptsDelayedFocus);
+ }
+
private CarAudioFocus getCarAudioFocus() {
CarAudioFocus carAudioFocus = new CarAudioFocus(mMockAudioManager, mMockPackageManager,
mFocusInteraction, TEST_CAR_AUDIO_CONTEXT, mMockCarVolumeInfoWrapper,
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioServiceUnitTest.java
index 052de81..d0585f0 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioServiceUnitTest.java
@@ -387,42 +387,42 @@
private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_VOLUME_INFO_0 =
- new CarVolumeGroupInfo.Builder("group id " + TEST_PRIMARY_ZONE_GROUP_0,
+ new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(true)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0).build();
private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0 =
- new CarVolumeGroupInfo.Builder("group id " + TEST_PRIMARY_ZONE_GROUP_0,
+ new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(false)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0).build();
private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_VOLUME_INFO_1 =
- new CarVolumeGroupInfo.Builder("group id " + TEST_PRIMARY_ZONE_GROUP_1,
+ new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_1,
PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1).setMuted(true)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_1)
.setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_0_VOLUME_INFO =
- new CarVolumeGroupInfo.Builder("group id " + TEST_SECONDARY_ZONE_GROUP_0,
+ new CarVolumeGroupInfo.Builder("config 0 group " + TEST_SECONDARY_ZONE_GROUP_0,
TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_DEFAULT)
.setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_0 =
- new CarVolumeGroupInfo.Builder("group id " + TEST_SECONDARY_ZONE_GROUP_0,
+ new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_0,
TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_0)
.setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_1 =
- new CarVolumeGroupInfo.Builder("group id " + TEST_SECONDARY_ZONE_GROUP_1,
+ new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_1,
TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_1)
.setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
.setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_1)
@@ -516,6 +516,7 @@
private TemporaryFile mTemporaryAudioConfigurationFile;
private TemporaryFile mTemporaryAudioConfigurationWithoutZoneMappingFile;
private TemporaryFile mTemporaryAudioConfigurationWithoutMirroringFile;
+ private TemporaryFile mTemporaryAudioConfigurationWithOEMContexts;
private Context mContext;
private AudioDeviceInfo mMicrophoneInputDevice;
private AudioDeviceInfo mFmTunerInputDevice;
@@ -555,6 +556,17 @@
}
try (InputStream configurationStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_using_oem_defined_context)) {
+ mTemporaryAudioConfigurationWithOEMContexts = new TemporaryFile("xml");
+ mTemporaryAudioConfigurationWithOEMContexts.write(
+ new String(configurationStream.readAllBytes()));
+ Log.i(TAG, "Temporary Car Audio Configuration with OEM Context File Location: "
+ + mTemporaryAudioConfigurationWithOEMContexts.getPath());
+ }
+
+
+
+ try (InputStream configurationStream = mContext.getResources().openRawResource(
R.raw.car_audio_configuration_without_zone_mapping)) {
mTemporaryAudioConfigurationWithoutZoneMappingFile = new TemporaryFile("xml");
mTemporaryAudioConfigurationWithoutZoneMappingFile
@@ -784,7 +796,7 @@
}
@Test
- public void getVolumeGroupCount_onPrimaryZone__withNonDynamicRouting_returnsAllGroups() {
+ public void getVolumeGroupCount_onPrimaryZone_withNonDynamicRouting_returnsAllGroups() {
when(mMockResources.getBoolean(audioUseDynamicRouting))
.thenReturn(/* useDynamicRouting= */ false);
CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
@@ -2324,6 +2336,22 @@
}
@Test
+ public void getVolumeGroupInfosForZone_forOEMConfiguration() {
+ CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
+ mTemporaryAudioConfigurationWithOEMContexts.getFile().getAbsolutePath(),
+ mCarVolumeCallbackHandler);
+ nonDynamicAudioService.init();
+
+ List<CarVolumeGroupInfo> infos =
+ nonDynamicAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
+
+ expectWithMessage("Car volume group infos size with OEM configuration")
+ .that(infos).hasSize(1);
+ expectWithMessage("Car volume group info name with OEM configuration")
+ .that(infos.get(0).getName()).isEqualTo("OEM_VOLUME_GROUP");
+ }
+
+ @Test
public void getVolumeGroupInfosForZone_size() {
mCarAudioService.init();
int groupCount = mCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
@@ -2522,6 +2550,33 @@
}
@Test
+ public void binderDied_onMediaRequestApprover_resetsApprovedRequest()
+ throws Exception {
+ mCarAudioService.init();
+ TestPrimaryZoneMediaAudioRequestCallback requestToken =
+ new TestPrimaryZoneMediaAudioRequestCallback();
+ TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
+ assignOccupantToAudioZones();
+ mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
+ long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
+ TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
+ requestToken.waitForCallback();
+ requestToken.reset();
+ mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
+ requestToken.waitForCallback();
+ requestCallback.waitForCallback();
+ requestCallback.reset();
+
+ requestToken.mDeathRecipient.binderDied();
+
+ requestCallback.waitForCallback();
+ expectWithMessage("Stopped status due to approver's death").that(requestCallback.mStatus)
+ .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
+ expectWithMessage("Stopped id due to approver's death")
+ .that(requestCallback.mRequestId).isEqualTo(requestId);
+ }
+
+ @Test
public void allowMediaAudioOnPrimaryZone_withAllowedRequest() throws Exception {
mCarAudioService.init();
TestPrimaryZoneMediaAudioRequestCallback
@@ -2755,6 +2810,53 @@
}
@Test
+ public void isMediaAudioAllowedInPrimaryZone_afterUserLogout() throws Exception {
+ mCarAudioService.init();
+ TestPrimaryZoneMediaAudioRequestCallback
+ requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
+ TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
+ assignOccupantToAudioZones();
+ mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
+ long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
+ TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
+ requestToken.waitForCallback();
+ requestToken.reset();
+ mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
+ requestToken.waitForCallback();
+ requestToken.reset();
+ simulateLogoutPassengers();
+ requestToken.waitForCallback();
+
+ expectWithMessage("Media allowed status after passenger logout")
+ .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
+ TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
+ }
+
+ @Test
+ public void isMediaAudioAllowedInPrimaryZone_afterUserSwitch() throws Exception {
+ mCarAudioService.init();
+ TestPrimaryZoneMediaAudioRequestCallback
+ requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
+ TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
+ assignOccupantToAudioZones();
+ mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
+ long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
+ TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
+ requestToken.waitForCallback();
+ requestToken.reset();
+ mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
+ /* allowed= */ true);
+ requestToken.waitForCallback();
+ requestToken.reset();
+ simulatePassengersSwitch();
+ requestToken.waitForCallback();
+
+ expectWithMessage("Media allowed status after passenger switch")
+ .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
+ TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
+ }
+
+ @Test
public void resetMediaAudioOnPrimaryZone_afterAllowed() throws Exception {
mCarAudioService.init();
TestPrimaryZoneMediaAudioRequestCallback
@@ -4199,6 +4301,120 @@
}
@Test
+ public void getMirrorAudioZonesForAudioZone_withoutMirroringEnabled()
+ throws Exception {
+ mCarAudioService.init();
+ assignOccupantToAudioZones();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
+
+ expectWithMessage("Mirroring zones for non mirror zone %s", TEST_REAR_RIGHT_ZONE_ID)
+ .that(zones).asList().isEmpty();
+ }
+
+ @Test
+ public void getMirrorAudioZonesForAudioZone_withMirroringEnabled() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
+
+ expectWithMessage("Mirroring zones for mirror zone %s", TEST_REAR_RIGHT_ZONE_ID).that(zones)
+ .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
+ }
+
+ @Test
+ public void getMirrorAudioZonesForAudioZone_afterDisableMirror() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+ callback.reset(1);
+ mCarAudioService.disableAudioMirror(requestId);
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
+
+ expectWithMessage("Mirroring zones for mirror zone %s after disabling mirroring",
+ TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
+ }
+
+ @Test
+ public void getMirrorAudioZonesForAudioZone_afterPassengerLogout() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+ callback.reset(1);
+ simulateLogoutRightPassengers();
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
+
+ expectWithMessage("Mirroring zones for mirror zone %s after logout",
+ TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
+ }
+
+ @Test
+ public void getMirrorAudioZonesForMirrorRequest_withMirroringEnabled() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(requestId);
+
+ expectWithMessage("Mirroring zones for mirror request %s", requestId).that(zones).asList()
+ .containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
+ }
+
+ @Test
+ public void getMirrorAudioZonesForMirrorRequest_afterDisableMirror() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+ callback.reset(1);
+ mCarAudioService.disableAudioMirror(requestId);
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(TEST_REAR_RIGHT_ZONE_ID);
+
+ expectWithMessage("Mirroring zones for mirror request %s after disabling mirroring",
+ requestId).that(zones).asList().isEmpty();
+ }
+
+ @Test
+ public void getMirrorAudioZonesForMirrorRequest_afterPassengerLogout() throws Exception {
+ mCarAudioService.init();
+ TestAudioZonesMirrorStatusCallbackCallback callback =
+ getAudioZonesMirrorStatusCallback();
+ assignOccupantToAudioZones();
+ long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
+ callback.waitForCallback();
+ callback.reset(1);
+ simulateLogoutRightPassengers();
+ callback.waitForCallback();
+
+ int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(requestId);
+
+ expectWithMessage("Mirroring zones for mirror request %s after logout",
+ TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
+ }
+
+ @Test
public void onAudioVolumeGroupChanged_dispatchCallbackEvent() throws RemoteException {
CarAudioService useCoreAudioCarAudioService =
getCarAudioServiceUsingCoreAudioRoutingAndVolume();
@@ -4375,6 +4591,8 @@
mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
/* mute= */ true, TEST_FLAGS);
+ volumeEventCallback.waitForCallback();
+ volumeEventCallback.reset();
reset(mCarVolumeCallbackHandler);
mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
@@ -4437,6 +4655,31 @@
CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
}
+ private void simulateLogoutPassengers() throws Exception {
+ when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
+ .thenReturn(UserManagerHelper.USER_NULL);
+ when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
+ .thenReturn(UserManagerHelper.USER_NULL);
+
+ assignOccupantToAudioZones();
+ }
+
+ private void simulateLogoutRightPassengers() throws Exception {
+ when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
+ .thenReturn(UserManagerHelper.USER_NULL);
+
+ assignOccupantToAudioZones();
+ }
+
+ private void simulatePassengersSwitch() throws Exception {
+ when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
+ .thenReturn(TEST_REAR_RIGHT_USER_ID);
+ when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
+ .thenReturn(TEST_REAR_LEFT_USER_ID);
+
+ assignOccupantToAudioZones();
+ }
+
private CarAudioGainConfigInfo createCarAudioGainConfigInfo(int zoneId,
String devicePortAddress, int volumeIndex) {
AudioGainConfigInfo configInfo = new AudioGainConfigInfo();
@@ -4760,6 +5003,13 @@
private CarOccupantZoneManager.OccupantZoneInfo mInfo;
private CountDownLatch mStatusLatch = new CountDownLatch(1);
private int mStatus;
+ private DeathRecipient mDeathRecipient;
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ mDeathRecipient = recipient;
+ super.linkToDeath(recipient, flags);
+ }
@Override
public void onRequestMediaOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info,
@@ -4796,7 +5046,7 @@
private long mRequestId = INVALID_REQUEST_ID;
private CarOccupantZoneManager.OccupantZoneInfo mInfo;
private int mStatus;
- private final CountDownLatch mStatusLatch = new CountDownLatch(1);
+ private CountDownLatch mStatusLatch = new CountDownLatch(1);
@Override
public void onMediaAudioRequestStatusChanged(
@@ -4812,6 +5062,13 @@
private void waitForCallback() throws Exception {
mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
+
+ private void reset() {
+ mInfo = null;
+ mRequestId = INVALID_REQUEST_ID;
+ mStatus = INVALID_STATUS;
+ mStatusLatch = new CountDownLatch(1);
+ }
}
private static final class TestAudioZonesMirrorStatusCallbackCallback extends
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/MediaRequestHandlerTest.java b/tests/carservice_unit_test/src/com/android/car/audio/MediaRequestHandlerTest.java
index 4e379a7..ffa0ed8 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/MediaRequestHandlerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/MediaRequestHandlerTest.java
@@ -469,6 +469,46 @@
}
@Test
+ public void getAssignedRequestIdForOccupantZoneId_withoutRequest() throws Exception {
+ long requestId = mMediaRequestHandler.getAssignedRequestIdForOccupantZoneId(
+ TEST_PASSENGER_OCCUPANT_ZONE_ID);
+
+ expectWithMessage("Request id for unset occupant zone id")
+ .that(requestId).isEqualTo(INVALID_REQUEST_ID);
+ }
+
+ @Test
+ public void getAssignedRequestIdForOccupantZoneId_withoutApproval() throws Exception {
+ mMediaRequestHandler
+ .registerPrimaryZoneMediaAudioRequestCallback(mTestZoneAudioRequestCallback);
+ mMediaRequestHandler.requestMediaAudioOnPrimaryZone(
+ mTestMediaRequestStatusCallback, TEST_PASSENGER_OCCUPANT);
+
+ long requestId = mMediaRequestHandler.getAssignedRequestIdForOccupantZoneId(
+ TEST_PASSENGER_OCCUPANT_ZONE_ID);
+
+ expectWithMessage("Unapproved request id for zone id").that(requestId)
+ .isEqualTo(INVALID_REQUEST_ID);
+ }
+
+ @Test
+ public void getAssignedRequestIdForOccupantZoneId_afterApproval() throws Exception {
+ mMediaRequestHandler
+ .registerPrimaryZoneMediaAudioRequestCallback(mTestZoneAudioRequestCallback);
+ long approvedRequestId = mMediaRequestHandler.requestMediaAudioOnPrimaryZone(
+ mTestMediaRequestStatusCallback, TEST_PASSENGER_OCCUPANT);
+ mMediaRequestHandler.acceptMediaAudioRequest(mTestZoneAudioRequestCallback,
+ approvedRequestId);
+ mTestMediaRequestStatusCallback.waitForCallback();
+
+ long requestId = mMediaRequestHandler.getAssignedRequestIdForOccupantZoneId(
+ TEST_PASSENGER_OCCUPANT_ZONE_ID);
+
+ expectWithMessage("Approved request id for zone id").that(requestId)
+ .isEqualTo(approvedRequestId);
+ }
+
+ @Test
public void isMediaAudioAllowedInPrimaryZone_withoutRequest() {
boolean allowed = mMediaRequestHandler
.isMediaAudioAllowedInPrimaryZone(TEST_PASSENGER_OCCUPANT);
diff --git a/tests/carservice_unit_test/src/com/android/car/bluetooth/BluetoothDeviceManagerTest.java b/tests/carservice_unit_test/src/com/android/car/bluetooth/BluetoothDeviceManagerTest.java
index cf587c0..5568024 100644
--- a/tests/carservice_unit_test/src/com/android/car/bluetooth/BluetoothDeviceManagerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/bluetooth/BluetoothDeviceManagerTest.java
@@ -99,9 +99,12 @@
BluetoothDeviceManager mDeviceManager;
// Tests assume the auto connecting devices only support MAP
- private static final String CONNECTION_STATE =
+ private static final String MAP_CLIENT_ACTION =
"android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
+ private static final String PAN_ACTION =
+ "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+
private ParcelUuid[] mLocalUuids = new ParcelUuid[] {
BluetoothUuid.MAP, BluetoothUuid.MNS};
private ParcelUuid[] mUuids = new ParcelUuid[] {
@@ -247,9 +250,9 @@
mMockContext.sendBroadcast(intent);
}
- private void sendConnectionStateChanged(BluetoothDevice device, int newState) {
+ private void sendConnectionStateChanged(String profile, BluetoothDevice device, int newState) {
Assert.assertTrue(mMockContext != null);
- Intent intent = new Intent(CONNECTION_STATE);
+ Intent intent = new Intent(profile);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
mMockContext.sendBroadcast(intent);
@@ -804,13 +807,16 @@
mDeviceManager.beginAutoConnecting();
- sendConnectionStateChanged(mDeviceList.get(0), BluetoothProfile.STATE_CONNECTED);
+ sendConnectionStateChanged(MAP_CLIENT_ACTION, mDeviceList.get(0),
+ BluetoothProfile.STATE_CONNECTED);
verify(mDeviceList.get(0), timeout(CONNECT_TIMEOUT_MS).times(1)).connect();
- sendConnectionStateChanged(mDeviceList.get(1), BluetoothProfile.STATE_CONNECTED);
+ sendConnectionStateChanged(MAP_CLIENT_ACTION, mDeviceList.get(1),
+ BluetoothProfile.STATE_CONNECTED);
verify(mDeviceList.get(1), timeout(CONNECT_TIMEOUT_MS).times(1)).connect();
- sendConnectionStateChanged(mDeviceList.get(2), BluetoothProfile.STATE_CONNECTED);
+ sendConnectionStateChanged(MAP_CLIENT_ACTION, mDeviceList.get(2),
+ BluetoothProfile.STATE_CONNECTED);
verify(mDeviceList.get(2), timeout(CONNECT_TIMEOUT_MS).times(1)).connect();
}
@@ -830,16 +836,36 @@
* - The device is added to the list. Related/configured trigger profiles are connected.
*/
@Test
- public void testReceiveDeviceConnection_deviceAdded() throws Exception {
+ public void testReceiveDeviceConnectionProfileNotPan_deviceAdded() throws Exception {
setPreconditionsAndStart(ADAPTER_STATE_ANY, EMPTY_SETTINGS_STRING, EMPTY_DEVICE_LIST);
BluetoothDevice device = createDevice(SINGLE_DEVICE_LIST.get(0));
- sendConnectionStateChanged(device, BluetoothProfile.STATE_CONNECTED);
+ sendConnectionStateChanged(MAP_CLIENT_ACTION, device, BluetoothProfile.STATE_CONNECTED);
assertDeviceList(SINGLE_DEVICE_LIST);
verify(device, times(1)).connect();
}
/**
* Preconditions:
+ * - The device manager is initialized, there are no devices in the list.
+ *
+ * Actions:
+ * - A connection action comes in for the PAN profile and the device's priority is
+ * CONNECTION_POLICY_ALLOWED.
+ *
+ * Outcome:
+ * - The device is added to the list. Related/configured trigger profiles are *not* connected.
+ */
+ @Test
+ public void testReceiveDeviceConnectionProfilePan_deviceAdded() throws Exception {
+ setPreconditionsAndStart(ADAPTER_STATE_ANY, EMPTY_SETTINGS_STRING, EMPTY_DEVICE_LIST);
+ BluetoothDevice device = createDevice(SINGLE_DEVICE_LIST.get(0));
+ sendConnectionStateChanged(PAN_ACTION, device, BluetoothProfile.STATE_CONNECTED);
+ assertDeviceList(SINGLE_DEVICE_LIST);
+ verify(device, never()).connect();
+ }
+
+ /**
+ * Preconditions:
* - The device manager is initialized, there are is one device in the list.
*
* Actions:
@@ -853,7 +879,7 @@
public void testReceiveDeviceDisconnection_listUnchanged() throws Exception {
setPreconditionsAndStart(ADAPTER_STATE_ANY, EMPTY_SETTINGS_STRING, SINGLE_DEVICE_LIST);
BluetoothDevice device = mDeviceList.get(0);
- sendConnectionStateChanged(device, BluetoothProfile.STATE_DISCONNECTED);
+ sendConnectionStateChanged(MAP_CLIENT_ACTION, device, BluetoothProfile.STATE_DISCONNECTED);
assertDeviceList(SINGLE_DEVICE_LIST);
}
diff --git a/tests/carservice_unit_test/src/com/android/car/evs/CarEvsServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/evs/CarEvsServiceUnitTest.java
index 4261e1f..dbf3a05 100644
--- a/tests/carservice_unit_test/src/com/android/car/evs/CarEvsServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/evs/CarEvsServiceUnitTest.java
@@ -18,12 +18,26 @@
import static android.car.evs.CarEvsManager.ERROR_NONE;
import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_ACTIVE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_INACTIVE;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_REQUESTED;
+import static android.car.evs.CarEvsManager.SERVICE_STATE_UNAVAILABLE;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_DRIVERVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_LEFTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_RIGHTVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW;
+import static android.car.evs.CarEvsManager.SERVICE_TYPE_USER_DEFINED;
import static android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED;
import static android.car.hardware.property.CarPropertyEvent.PROPERTY_EVENT_ERROR;
import static android.car.hardware.property.CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE;
import static com.android.car.CarLog.TAG_EVS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -31,6 +45,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
@@ -44,6 +59,7 @@
import android.car.evs.CarEvsBufferDescriptor;
import android.car.evs.CarEvsManager;
+import android.car.evs.CarEvsManager.CarEvsServiceType;
import android.car.evs.CarEvsManager.CarEvsStreamEvent;
import android.car.evs.CarEvsStatus;
import android.car.evs.ICarEvsStatusListener;
@@ -52,7 +68,10 @@
import android.car.hardware.property.CarPropertyEvent;
import android.car.hardware.property.ICarPropertyEventListener;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.test.mocks.JavaMockitoHelper;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.HardwareBuffer;
@@ -61,9 +80,14 @@
import android.hardware.automotive.vehicle.VehicleProperty;
import android.hardware.display.DisplayManager;
import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.view.Display;
import android.util.Log;
import com.android.car.BuiltinPackageDependency;
@@ -73,7 +97,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
@@ -81,7 +107,9 @@
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
+import java.util.concurrent.Semaphore;
/**
* <p>This class contains unit tests for the {@link CarEvsService}.
@@ -92,9 +120,11 @@
private static final String TAG = CarEvsServiceUnitTest.class.getSimpleName();
private static final String COMMAND_TO_USE_DEFAULT_CAMERA = "default";
- private static final String CAMERA_TO_USE = "/dev/video10";
- private static final String DEFAULT_CAMERA_ID = "/dev/default";
- private static final int INVALID_ARG = -1;
+ private static final String ALTERNATIVE_CAMERA_ID = "/dev/video10";
+ private static final String DEFAULT_REARVIEW_CAMERA_ID = "/dev/rearview";
+ private static final String DEFAULT_FRONTVIEW_CAMERA_ID = "/dev/frontview";
+ private static final String DEFAULT_LEFTVIEW_CAMERA_ID = "/dev/leftview";
+ private static final String DEFAULT_RIGHTVIEW_CAMERA_ID = "/dev/rightview";
private static final int SERVICE_STATE_UNKNOWN = -1;
private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
private static final CarPropertyValue<Integer> GEAR_SELECTION_PROPERTY_NEUTRAL =
@@ -106,6 +136,35 @@
private static final CarPropertyValue<Integer> CURRENT_GEAR_PROPERTY_REVERSE =
new CarPropertyValue<>(VehicleProperty.CURRENT_GEAR, VehicleArea.GLOBAL,
VehicleGear.GEAR_REVERSE);
+ private static final String VALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME =
+ "com.android.car.evs/com.android.car.evs.MockActivity";
+ // ComponentName.unflattenFromString() returns a null object on below String object because it
+ // does not contain an expected separator, '/'.
+ private static final String INVALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME = "InvalidComponentName";
+ // Minimum delay to receive callbacks.
+ private static final int DEFAULT_TIMEOUT_IN_MS = 100;
+ private static final String[] DEFAULT_SERVICE_CONFIGURATION = {
+ "serviceType=REARVIEW,cameraId=" + DEFAULT_REARVIEW_CAMERA_ID + ",activityName=" +
+ VALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME,
+ };
+ private static final String[] MULTIPLE_SERVICE_CONFIGURATIONS = {
+ "serviceType=REARVIEW,cameraId=" + DEFAULT_REARVIEW_CAMERA_ID + ",activityName=" +
+ VALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME,
+ "serviceType=FRONTVIEW,cameraId=" + DEFAULT_FRONTVIEW_CAMERA_ID,
+ "serviceType=LEFTVIEW,cameraId=" + DEFAULT_LEFTVIEW_CAMERA_ID,
+ "serviceType=RIGHTVIEW,cameraId=" + DEFAULT_RIGHTVIEW_CAMERA_ID,
+ };
+
+ private static final int SERVICE_TYPE_SHIFT = 24;
+ private static final int SERVICE_TYPE_MASK = (0xFF << SERVICE_TYPE_SHIFT);
+ private static final int DATA_MASK = ~SERVICE_TYPE_MASK;
+ private static final int MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY = 10;
+ private static final int MAXIMUM_FRAME_INTERVAL_IN_MS = 67; // 15 frames per seconds.
+ // Test-only service type to skip a service-type check in test result verifications.
+ private static final int SERVICE_TYPE_ANY = Integer.MAX_VALUE;
+ private static final int EVENT_TYPE_ANY = Integer.MAX_VALUE;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
@Mock private CarPropertyService mMockCarPropertyService;
@Mock private ClassLoader mMockClassLoader;
@@ -116,9 +175,13 @@
@Mock private EvsHalWrapperImpl mMockEvsHalWrapper;
@Mock private PackageManager mMockPackageManager;
@Mock private Resources mMockResources;
+ @Mock private Display mMockDisplay;
- @Captor private ArgumentCaptor<ICarPropertyEventListener> mGearSelectionListenerCaptor;
+ @Captor private ArgumentCaptor<DisplayManager.DisplayListener> mDisplayListenerCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
+ @Captor private ArgumentCaptor<ICarPropertyEventListener> mGearSelectionListenerCaptor;
+ @Captor private ArgumentCaptor<Intent> mIntentCaptor;
+ @Captor private ArgumentCaptor<StateMachine.HalCallback> mHalCallbackCaptor;
private CarEvsService mCarEvsService;
private Random mRandom = new Random();
@@ -134,31 +197,50 @@
.spyStatic(Binder.class)
.spyStatic(CarServiceUtils.class)
.spyStatic(CarEvsService.class)
+ .spyStatic(StateMachine.class)
.spyStatic(BuiltinPackageDependency.class);
}
+ @Rule
+ public TestName mTestName = new TestName();
+
@Before
public void setUp() throws Exception {
when(mMockContext.getPackageName()).thenReturn(SYSTEMUI_PACKAGE_NAME);
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockContext.getString(com.android.car.R.string.config_evsRearviewCameraId))
- .thenReturn(DEFAULT_CAMERA_ID);
+ .thenReturn(DEFAULT_REARVIEW_CAMERA_ID);
when(mMockContext.getSystemService(DisplayManager.class)).thenReturn(mMockDisplayManager);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ doNothing().when(mMockContext).startActivity(mIntentCaptor.capture());
- when(mMockResources.getString(com.android.car.R.string.config_evsCameraActivity))
- .thenReturn("evsCameraActivity");
when(mMockResources.getString(
com.android.internal.R.string.config_systemUIServiceComponent))
.thenReturn("com.android.systemui/com.android.systemui.SystemUIService");
+ if (mTestName.getMethodName().endsWith("InvalidEvsCameraActivity")) {
+ when(mMockResources.getString(com.android.car.R.string.config_evsCameraActivity))
+ .thenReturn(INVALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME);
+ } else if (mTestName.getMethodName().endsWith("DeprecatedConfiguration")) {
+ when(mMockResources.getString(com.android.car.R.string.config_evsCameraActivity))
+ .thenReturn(VALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME);
+ } else if (mTestName.getMethodName().endsWith("WithMultipleServiceConfigurations")) {
+ when(mMockResources.getStringArray(com.android.car.R.array.config_carEvsService))
+ .thenReturn(MULTIPLE_SERVICE_CONFIGURATIONS);
+ } else {
+ // By default, we're using a new service configuration only with a rearview entry.
+ when(mMockResources.getStringArray(com.android.car.R.array.config_carEvsService))
+ .thenReturn(DEFAULT_SERVICE_CONFIGURATION);
+ }
+
when(mMockBuiltinPackageContext.getClassLoader()).thenReturn(mMockClassLoader);
doReturn(EvsHalWrapperImpl.class).when(mMockClassLoader)
.loadClass(BuiltinPackageDependency.EVS_HAL_WRAPPER_CLASS);
mockEvsHalService();
mockEvsHalWrapper();
- doReturn(mMockEvsHalWrapper).when(() -> CarEvsService.createHalWrapper(any(), any()));
+ doReturn(mMockEvsHalWrapper).when(
+ () -> StateMachine.createHalWrapper(any(), mHalCallbackCaptor.capture()));
// Get the property listener
when(mMockCarPropertyService
@@ -177,9 +259,8 @@
@Test
public void testIsSupported() throws Exception {
- assertThat(mCarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isTrue();
- assertThat(mCarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW)).isFalse();
- assertThrows(IllegalArgumentException.class, () -> mCarEvsService.isSupported(INVALID_ARG));
+ assertThat(mCarEvsService.isSupported(SERVICE_TYPE_REARVIEW)).isTrue();
+ assertThat(mCarEvsService.isSupported(SERVICE_TYPE_SURROUNDVIEW)).isFalse();
}
@Test
@@ -198,8 +279,8 @@
@Test
public void testGetCurrentStatus() {
CarEvsStatus status = mCarEvsService.getCurrentStatus();
- assertThat(status.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
- assertThat(status.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ assertThat(status.getServiceType()).isEqualTo(SERVICE_TYPE_REARVIEW);
+ assertThat(status.getState()).isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
@@ -217,16 +298,16 @@
when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
mCarEvsService.init();
- verify(mMockEvsHalWrapper, times(3)).init();
+ verify(mMockEvsHalWrapper, times(2)).init();
when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt())).thenReturn(null);
mCarEvsService.init();
- verify(mMockEvsHalWrapper, times(4)).init();
+ verify(mMockEvsHalWrapper, times(2)).init();
when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
.thenReturn(GEAR_SELECTION_PROPERTY_NEUTRAL);
mCarEvsService.init();
- verify(mMockEvsHalWrapper, times(5)).init();
+ verify(mMockEvsHalWrapper, times(2)).init();
}
@Test
@@ -241,21 +322,50 @@
@Test
public void testOnEvent() {
- mCarEvsService.onEvent(CarEvsManager.SERVICE_TYPE_REARVIEW, /* on= */ true);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ // Create a buffer to circulate
+ HardwareBuffer buffer =
+ HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
+ /* format= */ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ int bufferId = mRandom.nextInt() & DATA_MASK;
+ EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.onEvent(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW, /* on= */ true);
+ // Request a REARVIEW via HAL Event. CarEvsService should enter REQUESTED state.
+ mCarEvsService.mEvsTriggerListener.onEvent(SERVICE_TYPE_REARVIEW, /* on= */ true);
+ assertThat(spiedStatusListener.waitFor(SERVICE_TYPE_REARVIEW, SERVICE_STATE_REQUESTED))
+ .isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
- mCarEvsService.onEvent(CarEvsManager.SERVICE_TYPE_REARVIEW, /* on= */ false);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ // Request a video stream with a given token. CarEvsService should enter ACTIVE state and
+ // gives a callback upon the frame event.
+ Bundle extras = mIntentCaptor.getValue().getExtras();
+ assertThat(extras).isNotNull();
+ IBinder token = extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW, token, spiedCallback))
+ .isEqualTo(ERROR_NONE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE))
+ .isTrue();
- mCarEvsService.onEvent(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW, /* on= */ false);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedCallback.waitForFrames(/* expected= */ 1)).isTrue();
+
+ // Request stopping a current activity. CarEvsService should give us a callback with
+ // STREAM_STOPPED event and ehter INACTIVE state.
+ mCarEvsService.mEvsTriggerListener.onEvent(SERVICE_TYPE_REARVIEW, /* on= */ false);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED)).isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
+
+
+ // Request an unsupported service. CarEvsService should decline a request and stay at the
+ // same state.
+ mCarEvsService.mEvsTriggerListener.onEvent(SERVICE_TYPE_SURROUNDVIEW, /* on= */ true);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isFalse();
}
@Test
@@ -266,81 +376,89 @@
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- int bufferId = mRandom.nextInt();
- mCarEvsService.setStreamCallback(null);
- mCarEvsService.onFrameEvent(bufferId, buffer);
+ int bufferId = mRandom.nextInt() & DATA_MASK;
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, null);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+
+ SystemClock.sleep(DEFAULT_TIMEOUT_IN_MS);
verify(mMockEvsHalWrapper).doneWithFrame(anyInt());
// Nothing to verify from below line but added to increase the code coverage.
- mCarEvsService.onHalEvent(/* eventType= */ 0);
+ mHalCallbackCaptor.getValue().onHalEvent(/* eventType= */ 0);
}
@Test
public void testHalDeath() {
- mCarEvsService.onHalDeath();
+ mHalCallbackCaptor.getValue().onHalDeath();
verify(mMockEvsHalWrapper, times(2)).connectToHalServiceIfNecessary();
}
@Test
public void testTransitionFromUnavailableToInactive() {
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
mCarEvsService.stopActivity();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
- verify(mMockEvsHalWrapper, times(2)).connectToHalServiceIfNecessary();
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
+ verify(mMockEvsHalWrapper).connectToHalServiceIfNecessary();
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.setStreamCallback(null);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, null);
when(mMockEvsHalWrapper.connectToHalServiceIfNecessary()).thenReturn(false);
- mCarEvsService.onEvent(CarEvsManager.SERVICE_TYPE_REARVIEW, /* on= */ false);
- verify(mMockEvsHalWrapper, times(3)).connectToHalServiceIfNecessary();
+ mCarEvsService.mEvsTriggerListener.onEvent(SERVICE_TYPE_REARVIEW, /* on= */ false);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isFalse();
+ verify(mMockEvsHalWrapper, atLeastOnce()).connectToHalServiceIfNecessary();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.setStreamCallback(spiedCallback);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, spiedCallback);
mCarEvsService.stopVideoStream(spiedCallback);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
- verify(spiedCallback, times(3)).asBinder();
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
when(spiedCallback.asBinder()).thenReturn(null);
- mCarEvsService.setStreamCallback(spiedCallback);
- mCarEvsService.stopVideoStream(spiedCallback);
+ assertThrows(NullPointerException.class,
+ () -> mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, spiedCallback));
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
- verify(spiedCallback, times(6)).asBinder();
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
mCarEvsService.stopActivity();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
}
@Test
public void testTransitionFromInactiveToInactive() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
mCarEvsService.stopActivity();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
public void testTransitionFromActiveToInactive() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setStreamCallback(null);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, null);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
}
@Test
public void testTransitionFromUnknownToInactive() {
- mCarEvsService.setServiceState(SERVICE_STATE_UNKNOWN);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNKNOWN);
IllegalStateException thrown = assertThrows(IllegalStateException.class,
- () -> mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW));
+ () -> mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW));
assertWithMessage("Verify current status of CarEvsService")
.that(thrown).hasMessageThat()
.contains("CarEvsService is in the unknown state.");
@@ -349,14 +467,17 @@
@Test
public void testTransitionFromUnavailableToActive() {
EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
+ .startVideoStream(SERVICE_TYPE_REARVIEW,
/* token= */ null, streamCallback))
- .isEqualTo(ERROR_UNAVAILABLE);
+ .isEqualTo(ERROR_NONE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
}
@Test
@@ -365,30 +486,31 @@
EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_NONE);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isTrue();
verify(spiedStatusListener).onStatusChanged(argThat(
- received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE));
+ received -> received.getState() == SERVICE_STATE_ACTIVE));
when(mMockEvsHalWrapper.requestToStartVideoStream()).thenReturn(false);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
when(mMockEvsHalWrapper.openCamera(anyString())).thenReturn(false);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
when(mMockEvsHalWrapper.requestToStartVideoStream()).thenReturn(true);
when(mMockEvsHalWrapper.openCamera(anyString())).thenReturn(true);
@@ -400,45 +522,49 @@
EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_REQUESTED);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_REQUESTED);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_NONE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
verify(spiedStatusListener).onStatusChanged(argThat(
- received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE));
+ received -> received.getState() == SERVICE_STATE_ACTIVE));
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_REQUESTED);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_REQUESTED);
assertThrows(NullPointerException.class,
() -> mCarEvsService.startVideoStream(
- CarEvsManager.SERVICE_TYPE_REARVIEW, null, null));
+ SERVICE_TYPE_REARVIEW, null, null));
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_REQUESTED);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_REQUESTED);
when(mMockEvsHalWrapper.openCamera(anyString())).thenReturn(false);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
public void testTransitionFromActiveToActive() {
EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setStreamCallback(streamCallback);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, streamCallback))
.isEqualTo(ERROR_NONE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
// Transition from unknown states
- mCarEvsService.setServiceState(SERVICE_STATE_UNKNOWN);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNKNOWN);
IllegalStateException thrown = assertThrows(IllegalStateException.class,
- () -> mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW));
+ () -> mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW));
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isFalse();
assertWithMessage("Verify current status of CarEvsService")
.that(thrown).hasMessageThat()
.contains("CarEvsService is in the unknown state.");
@@ -449,57 +575,61 @@
EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
when(mMockEvsHalWrapper.connectToHalServiceIfNecessary()).thenReturn(false);
- assertThat(mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
+ assertThat(mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_UNAVAILABLE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ .isEqualTo(SERVICE_STATE_UNAVAILABLE);
when(mMockEvsHalWrapper.connectToHalServiceIfNecessary()).thenReturn(true);
- assertThat(mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
+ assertThat(mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_NONE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
+
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
verify(spiedStatusListener).onStatusChanged(argThat(
- received -> received.getState() == CarEvsManager.SERVICE_STATE_REQUESTED));
+ received -> received.getState() == SERVICE_STATE_REQUESTED));
}
@Test
public void testTransitionFromInactiveToRequested() {
EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
- assertThat(mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
+ assertThat(mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_NONE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
verify(spiedStatusListener).onStatusChanged(argThat(
- received -> received.getState() == CarEvsManager.SERVICE_STATE_REQUESTED));
+ received -> received.getState() == SERVICE_STATE_REQUESTED));
}
@Test
public void testTransitionFromActiveToRequested() {
+ EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback);
mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
- assertThat(mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
+ assertThat(mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_NONE);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
verify(spiedStatusListener).onStatusChanged(argThat(
- received -> received.getState() == CarEvsManager.SERVICE_STATE_REQUESTED));
+ received -> received.getState() == SERVICE_STATE_REQUESTED));
}
@Test
public void testTransitionFromUnknownToRequested() {
- mCarEvsService.setServiceState(SERVICE_STATE_UNKNOWN);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNKNOWN);
IllegalStateException thrown = assertThrows(IllegalStateException.class,
- () -> mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW));
+ () -> mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW));
assertWithMessage("Verify current status of CarEvsService")
.that(thrown).hasMessageThat()
.contains("CarEvsService is in the unknown state.");
@@ -518,26 +648,12 @@
mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList(event));
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
- public void testEmptyCarPropertyEvent() throws Exception {
- when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
- when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
- .thenReturn(GEAR_SELECTION_PROPERTY_REVERSE);
-
- mCarEvsService.setToUseGearSelection(/* useGearSelection= */ true);
- mCarEvsService.init();
- mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList());
-
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
- }
-
- @Test
- public void testNonPropertyChangeEvent() throws Exception {
- CarPropertyEvent event = new CarPropertyEvent(PROPERTY_EVENT_ERROR,
+ public void testCarPropertyEventWithInvalidEvsCameraActivity() throws Exception {
+ CarPropertyEvent event = new CarPropertyEvent(PROPERTY_EVENT_PROPERTY_CHANGE,
GEAR_SELECTION_PROPERTY_REVERSE);
when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
@@ -548,44 +664,127 @@
mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList(event));
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
- public void testNonGearSelectionProperty() throws Exception {
- CarPropertyEvent event = new CarPropertyEvent(PROPERTY_EVENT_PROPERTY_CHANGE,
- CURRENT_GEAR_PROPERTY_REVERSE);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
+ public void testDuplicatedDisplayEvent() throws Exception {
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
+ when(mMockDisplayManager.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mMockDisplay);
+ when(mMockDisplay.getState()).thenReturn(Display.STATE_ON);
+
+ // Set a last HAL event to require a camera activity and give onDisplayChanged() callback
+ // twice.
+ mCarEvsService.init();
+ mCarEvsService.setLastEvsHalEvent(/* timestamp= */ 0, SERVICE_TYPE_REARVIEW,
+ /* on= */ true);
+ mCarEvsService.setToUseGearSelection(/* useGearSelection= */ true);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+
+ verify(mMockDisplayManager).registerDisplayListener(
+ mDisplayListenerCaptor.capture(), any(Handler.class));
+
+ mDisplayListenerCaptor.getValue().onDisplayChanged(Display.DEFAULT_DISPLAY);
+ mDisplayListenerCaptor.getValue().onDisplayChanged(Display.DEFAULT_DISPLAY);
+
+ // Confirm that we have received onStatusChanged() callback only once.
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
+ verify(spiedStatusListener).onStatusChanged(argThat(
+ received -> received.getState() == SERVICE_STATE_REQUESTED));
+ verify(mMockContext).startActivity(any());
+
+ // Also, verify that we have received an intent for a camera activity.
+ assertThat(mIntentCaptor.getValue().getComponent()).isEqualTo(
+ ComponentName.unflattenFromString(VALID_EVS_CAMERA_ACTIVITY_COMPONENT_NAME));
+
+ Bundle extras = mIntentCaptor.getValue().getExtras();
+ assertThat(extras).isNotNull();
+ assertThat(extras.getBinder(CarEvsManager.EXTRA_SESSION_TOKEN)).isNotNull();
+ }
+
+ @Test
+ public void testEmptyCarPropertyEvent() throws Exception {
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
.thenReturn(GEAR_SELECTION_PROPERTY_REVERSE);
mCarEvsService.setToUseGearSelection(/* useGearSelection= */ true);
mCarEvsService.init();
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList());
+
+ // No state change is expected.
+ assertThat(spiedStatusListener.waitFor()).isFalse();
+ assertThat(mCarEvsService.getCurrentStatus().getState())
+ .isEqualTo(SERVICE_STATE_INACTIVE);
+ }
+
+ @Test
+ public void testNonPropertyChangeEvent() throws Exception {
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ CarPropertyEvent event = new CarPropertyEvent(PROPERTY_EVENT_ERROR,
+ GEAR_SELECTION_PROPERTY_REVERSE);
+ when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
+ when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
+ .thenReturn(GEAR_SELECTION_PROPERTY_REVERSE);
+
+ mCarEvsService.setToUseGearSelection(/* useGearSelection= */ true);
+ mCarEvsService.init();
+ mCarEvsService.registerStatusListener(spiedStatusListener);
mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList(event));
+ // No state change is expected.
+ assertThat(spiedStatusListener.waitFor()).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
+ }
+
+ @Test
+ public void testNonGearSelectionProperty() throws Exception {
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ CarPropertyEvent event = new CarPropertyEvent(PROPERTY_EVENT_PROPERTY_CHANGE,
+ CURRENT_GEAR_PROPERTY_REVERSE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+ when(mMockEvsHalService.isEvsServiceRequestSupported()).thenReturn(false);
+ when(mMockCarPropertyService.getPropertySafe(anyInt(), anyInt()))
+ .thenReturn(GEAR_SELECTION_PROPERTY_REVERSE);
+
+ mCarEvsService.setToUseGearSelection(/* useGearSelection= */ true);
+ mCarEvsService.init();
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mGearSelectionListenerCaptor.getValue().onEvent(Arrays.asList(event));
+
+ // No state change is expected.
+ assertThat(spiedStatusListener.waitFor()).isFalse();
+ assertThat(mCarEvsService.getCurrentStatus().getState())
+ .isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
public void testStartActivity() {
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
- int bufferId = mRandom.nextInt();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+ int bufferId = mRandom.nextInt() & DATA_MASK;
HardwareBuffer buffer =
HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
+
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, spiedCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, spiedCallback))
.isEqualTo(ERROR_NONE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isTrue();
- mCarEvsService.onFrameEvent(bufferId, buffer);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedCallback.waitForFrames(/* expected= */ 1)).isTrue();
verify(spiedCallback)
.onNewFrame(argThat(received -> received.getId() == bufferId));
}
@@ -593,77 +792,130 @@
@Test
public void testStartActivityFromUnavailableState() {
when(mMockEvsHalWrapper.connectToHalServiceIfNecessary()).thenReturn(true);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
verify(mMockEvsHalWrapper, times(2)).connectToHalServiceIfNecessary();
}
@Test
public void testStartActivityFromInactiveState() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_INACTIVE);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
public void testStartActivityFromActiveState() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
public void testStartActivityFromRequestedState() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_REQUESTED);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_REQUESTED);
+
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isFalse();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
public void testRequestDifferentServiceInRequestedState() {
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
- assertThat(mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW))
- .isEqualTo(ERROR_NONE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
+
+ assertThat(mCarEvsService.startActivity(SERVICE_TYPE_SURROUNDVIEW))
+ .isEqualTo(ERROR_UNAVAILABLE);
+ assertThat(mCarEvsService.getCurrentStatus().getState())
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
public void testStartAndStopActivity() {
- mCarEvsService.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW);
- assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
- assertThat(mCarEvsService.getCurrentStatus().getServiceType())
- .isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
+ // Create a buffer to circulate
+ HardwareBuffer buffer =
+ HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
+ /* format= */ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ int bufferId = mRandom.nextInt() & DATA_MASK;
+ EvsStreamCallbackImpl spiedStreamCallback = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
- mCarEvsService.stopActivity();
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, spiedStreamCallback);
+
+ // Request starting an activity. CarEvsService should enter REQUESTED state.
+ mCarEvsService.startActivity(SERVICE_TYPE_REARVIEW);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_REQUESTED)).isTrue();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
+ assertThat(mCarEvsService.getCurrentStatus().getServiceType())
+ .isEqualTo(SERVICE_TYPE_REARVIEW);
+
+ // Request a video stream with a given token. CarEvsService should enter ACTIVE state and
+ // gives a callback upon the frame event.
+ Bundle extras = mIntentCaptor.getValue().getExtras();
+ assertThat(extras).isNotNull();
+
+ int type = extras.getShort(Integer.toString(SERVICE_TYPE_REARVIEW));
+ assertThat(mCarEvsService.startVideoStream(type, /* token= */ null, spiedStreamCallback))
+ .isEqualTo(ERROR_NONE);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_ACTIVE)).isTrue();
+
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedStreamCallback.waitForFrames(/* expected= */ 1)).isTrue();
+
+ // Request stopping a current activity. CarEvsService should give us a callback with
+ // STREAM_STOPPED event and ehter INACTIVE state.
+ mCarEvsService.stopVideoStream(spiedStreamCallback);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ assertThat(spiedStreamCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(mCarEvsService.getCurrentStatus().getState())
+ .isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
public void testStopVideoStream() {
EvsStreamCallbackImpl streamCallback0 = new EvsStreamCallbackImpl();
EvsStreamCallbackImpl streamCallback1 = new EvsStreamCallbackImpl();
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setStreamCallback(null);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, null);
mCarEvsService.stopVideoStream(streamCallback1);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setStreamCallback(streamCallback0);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, streamCallback0);
mCarEvsService.stopVideoStream(streamCallback1);
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
+ .isEqualTo(SERVICE_STATE_ACTIVE);
}
@Test
@@ -677,18 +929,20 @@
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- int bufferId = mRandom.nextInt();
+ int bufferId = mRandom.nextInt() & DATA_MASK;
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, spiedStreamCallback0))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, spiedStreamCallback0))
.isEqualTo(ERROR_NONE);
- mCarEvsService.onFrameEvent(bufferId, buffer);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
- int anotherBufferId = mRandom.nextInt();
+ int anotherBufferId = mRandom.nextInt() & DATA_MASK;
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, null, spiedStreamCallback1))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, null, spiedStreamCallback1))
.isEqualTo(ERROR_NONE);
- mCarEvsService.onFrameEvent(anotherBufferId, buffer);
+ mHalCallbackCaptor.getValue().onFrameEvent(anotherBufferId, buffer);
+ assertThat(spiedStreamCallback0.waitForFrames(/* expected= */ 1)).isTrue();
+ assertThat(spiedStreamCallback1.waitForFrames(/* expected= */ 1)).isTrue();
verify(spiedStreamCallback0)
.onNewFrame(argThat(received -> received.getId() == bufferId));
verify(spiedStreamCallback1)
@@ -703,14 +957,15 @@
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- int bufferId = mRandom.nextInt();
+ int bufferId = mRandom.nextInt() & DATA_MASK;
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
+ .startVideoStream(SERVICE_TYPE_REARVIEW,
/* token= */ null, spiedCallback))
.isEqualTo(ERROR_NONE);
- mCarEvsService.onFrameEvent(bufferId, buffer);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedCallback.waitForFrames(/* expected= */ 1)).isTrue();
verify(spiedCallback)
.onNewFrame(argThat(received -> received.getId() == bufferId));
mCarEvsService.stopVideoStream(spiedCallback);
@@ -732,17 +987,14 @@
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- int bufferId = mRandom.nextInt();
+ int bufferId = mRandom.nextInt() & DATA_MASK;
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, token, spiedCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, token, spiedCallback))
.isEqualTo(ERROR_NONE);
- mCarEvsService.onFrameEvent(bufferId, buffer);
- doAnswer(args -> {
- mCarEvsService.returnFrameBuffer(new CarEvsBufferDescriptor(bufferId, buffer));
- return true;
- }).when(spiedCallback).onNewFrame(any());
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedCallback.waitForFrames(/* expected= */ 1)).isTrue();
mCarEvsService.stopVideoStream(spiedCallback);
verify(spiedCallback)
.onNewFrame(argThat(received -> received.getId() == bufferId));
@@ -754,7 +1006,7 @@
assertThat(token).isNotNull();
EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW, token, streamCallback))
+ .startVideoStream(SERVICE_TYPE_SURROUNDVIEW, token, streamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
}
@@ -765,55 +1017,68 @@
assertThat(token).isNotNull();
EvsStreamCallbackImpl streamCallback = new EvsStreamCallbackImpl();
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, token, streamCallback))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, token, streamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
}
@Test
public void testCommandToSetRearviewCameraId() {
- assertThat(mCarEvsService.getRearviewCameraIdFromCommand()).isEqualTo(DEFAULT_CAMERA_ID);
- assertThat(mCarEvsService.setRearviewCameraIdFromCommand(CAMERA_TO_USE)).isTrue();
- assertThat(mCarEvsService.getRearviewCameraIdFromCommand()).isEqualTo(CAMERA_TO_USE);
+ assertThat(mCarEvsService.getRearviewCameraIdFromCommand())
+ .isEqualTo(DEFAULT_REARVIEW_CAMERA_ID);
+ assertThat(mCarEvsService.setRearviewCameraIdFromCommand(ALTERNATIVE_CAMERA_ID)).isTrue();
+ assertThat(mCarEvsService.getRearviewCameraIdFromCommand())
+ .isEqualTo(ALTERNATIVE_CAMERA_ID);
assertThat(mCarEvsService.setRearviewCameraIdFromCommand(COMMAND_TO_USE_DEFAULT_CAMERA))
.isTrue();
- assertThat(mCarEvsService.getRearviewCameraIdFromCommand()).isEqualTo(DEFAULT_CAMERA_ID);
+ assertThat(mCarEvsService.getRearviewCameraIdFromCommand())
+ .isEqualTo(DEFAULT_REARVIEW_CAMERA_ID);
}
@Test
public void testHalEvents() {
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
- mCarEvsService.setStreamCallback(spiedCallback);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, spiedCallback);
- mCarEvsService.onHalEvent(0);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_NONE);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_NONE)).isTrue();
+ verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_NONE);
+
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_STREAM_STARTED);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STARTED)).isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_STREAM_STARTED);
- mCarEvsService.onHalEvent(1);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED)).isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
- mCarEvsService.onHalEvent(2);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_FRAME_DROPPED);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_FRAME_DROPPED)).isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_FRAME_DROPPED);
- mCarEvsService.onHalEvent(3);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_TIMEOUT);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_TIMEOUT)).isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_TIMEOUT);
- mCarEvsService.onHalEvent(4);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_PARAMETER_CHANGED);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_PARAMETER_CHANGED))
+ .isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_PARAMETER_CHANGED);
- mCarEvsService.onHalEvent(5);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_PRIMARY_OWNER_CHANGED);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_PRIMARY_OWNER_CHANGED))
+ .isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_PRIMARY_OWNER_CHANGED);
- mCarEvsService.onHalEvent(6);
+ mHalCallbackCaptor.getValue().onHalEvent(CarEvsManager.STREAM_EVENT_OTHER_ERRORS);
+ assertThat(spiedCallback.waitForEvent(CarEvsManager.STREAM_EVENT_OTHER_ERRORS)).isTrue();
verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_OTHER_ERRORS);
-
- mCarEvsService.onHalEvent(7);
- verify(spiedCallback).onStreamEvent(CarEvsManager.STREAM_EVENT_NONE);
}
@Test
public void testHandleStreamCallbackCrash() throws Exception {
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
+ .startVideoStream(SERVICE_TYPE_REARVIEW,
/* token= */ null, spiedCallback))
.isEqualTo(ERROR_NONE);
@@ -825,14 +1090,14 @@
binderDeathRecipient.binderDied();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
}
@Test
public void testHandleStreamCallbackCrashAndRequestActivity() throws Exception {
EvsStreamCallbackImpl spiedCallback = spy(new EvsStreamCallbackImpl());
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
+ .startVideoStream(SERVICE_TYPE_REARVIEW,
/* token= */ null, spiedCallback))
.isEqualTo(ERROR_NONE);
@@ -842,12 +1107,12 @@
assertWithMessage("CarEvsService binder death recipient")
.that(binderDeathRecipient).isNotNull();
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setLastEvsHalEvent(/* timestamp= */ 0, CarEvsManager.SERVICE_TYPE_REARVIEW,
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.setLastEvsHalEvent(/* timestamp= */ 0, SERVICE_TYPE_REARVIEW,
/* on= */ true);
binderDeathRecipient.binderDied();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_REQUESTED);
+ .isEqualTo(SERVICE_STATE_REQUESTED);
}
@Test
@@ -863,7 +1128,7 @@
binderDeathRecipient.binderDied();
assertThat(mCarEvsService.getCurrentStatus().getState())
- .isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
+ .isEqualTo(SERVICE_STATE_INACTIVE);
mCarEvsService.unregisterStatusListener(spiedListener);
}
@@ -880,22 +1145,261 @@
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- int bufferId = mRandom.nextInt();
+ int bufferId = mRandom.nextInt() & DATA_MASK;
EvsStreamCallbackImpl spiedStreamCallback0 = spy(new EvsStreamCallbackImpl());
EvsStreamCallbackImpl spiedStreamCallback1 = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
// Configures the service to be in its ACTIVE state and use a spied stream callback.
- mCarEvsService.setServiceState(CarEvsManager.SERVICE_STATE_ACTIVE);
- mCarEvsService.setStreamCallback(spiedStreamCallback0);
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_ACTIVE);
+ mCarEvsService.addStreamCallback(SERVICE_TYPE_REARVIEW, spiedStreamCallback0);
+ mCarEvsService.registerStatusListener(spiedStatusListener);
// Requests starting the rearview video stream.
assertThat(mCarEvsService
- .startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW, token, spiedStreamCallback1))
+ .startVideoStream(SERVICE_TYPE_REARVIEW, token, spiedStreamCallback1))
.isEqualTo(ERROR_NONE);
- mCarEvsService.onFrameEvent(bufferId, buffer);
- verify(spiedStreamCallback0, timeout(3000)).onStreamEvent(STREAM_EVENT_STREAM_STOPPED);
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+ assertThat(spiedStreamCallback0.waitForFrames(/* expected= */ 1)).isTrue();
+ assertThat(spiedStreamCallback1.waitForFrames(/* expected= */ 1)).isTrue();
+ verify(spiedStreamCallback0)
+ .onNewFrame(argThat(received -> received.getId() == bufferId));
verify(spiedStreamCallback1)
.onNewFrame(argThat(received -> received.getId() == bufferId));
+
+ mCarEvsService.stopVideoStream(spiedStreamCallback1);
+ assertThat(spiedStreamCallback1.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
+
+ mCarEvsService.stopVideoStream(spiedStreamCallback0);
+ assertThat(spiedStreamCallback0.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ }
+
+ @Test
+ public void testTwoConcurrentStreamClients() throws Exception {
+ HardwareBuffer buffer =
+ HardwareBuffer.create(
+ /* width= */ 64,
+ /* height= */ 32,
+ /* format= */ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ int bufferId = mRandom.nextInt() & DATA_MASK;
+ EvsStreamCallbackImpl spiedCallback0 = spy(new EvsStreamCallbackImpl());
+ EvsStreamCallbackImpl spiedCallback1 = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
+ // Configure the service to be in the inactive state.
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+
+ // Register a status listener and request starting video streams.
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW,
+ /* token= */ null, spiedCallback0)).isEqualTo(ERROR_NONE);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW,
+ /* token= */ null, spiedCallback1)).isEqualTo(ERROR_NONE);
+
+ // Verify that the service entered the active state.
+ verify(spiedStatusListener).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE));
+
+ // Send a buffer.
+ mHalCallbackCaptor.getValue().onFrameEvent(bufferId, buffer);
+
+ // Confirm that a buffer is forwarded to both clients.
+ assertThat(spiedCallback0.waitForFrames(/* expected= */ 1)).isTrue();
+ assertThat(spiedCallback1.waitForFrames(/* expected= */ 1)).isTrue();
+
+ // Stop a video stream for the first client and verify that the service still in the active
+ // state.
+ mCarEvsService.stopVideoStream(spiedCallback0);
+ assertThat(spiedCallback0.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED)).isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
+ assertThat(mCarEvsService.getCurrentStatus().getState()).isEqualTo(SERVICE_STATE_ACTIVE);
+
+ // Stop a video stream for the second client and verify that the service entered the
+ // inactive state.
+ mCarEvsService.stopVideoStream(spiedCallback1);
+ assertThat(spiedCallback1.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED)).isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ assertThat(mCarEvsService.getCurrentStatus().getState()).isEqualTo(SERVICE_STATE_INACTIVE);
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == SERVICE_STATE_ACTIVE ||
+ received.getState() == SERVICE_STATE_INACTIVE));
+ }
+
+ @Test
+ public void testCommandToSetCameraIdWithMultipleServiceConfigurations() {
+ String[] types = {"REARVIEW", "FRONTVIEW", "LEFTVIEW", "RIGHTVIEW"};
+ String[] cameraIds = {DEFAULT_REARVIEW_CAMERA_ID, DEFAULT_FRONTVIEW_CAMERA_ID,
+ DEFAULT_LEFTVIEW_CAMERA_ID, DEFAULT_RIGHTVIEW_CAMERA_ID};
+ for (int i = 0; i < types.length; i++) {
+ assertThat(mCarEvsService.getCameraIdFromCommand(types[i]))
+ .isEqualTo(cameraIds[i]);
+ assertThat(mCarEvsService.setCameraIdFromCommand(types[i], ALTERNATIVE_CAMERA_ID))
+ .isTrue();
+ assertThat(mCarEvsService.getCameraIdFromCommand(types[i]))
+ .isEqualTo(ALTERNATIVE_CAMERA_ID);
+ }
+ }
+
+ @Test
+ public void testTwoConcurrentStreamsWithMultipleServiceConfigurations() throws Exception {
+ HardwareBuffer buffer =
+ HardwareBuffer.create(
+ /* width= */ 64,
+ /* height= */ 32,
+ /* format= */ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ EvsStreamCallbackImpl spiedRearviewCallback = spy(new EvsStreamCallbackImpl());
+ EvsStreamCallbackImpl spiedFrontviewCallback = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
+ // Configure the service to be in the inactive state.
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_FRONTVIEW, SERVICE_STATE_INACTIVE);
+
+ // Register a status listener and request starting video streams.
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW,
+ /* token= */ null, spiedRearviewCallback)).isEqualTo(ERROR_NONE);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_FRONTVIEW,
+ /* token= */ null, spiedFrontviewCallback)).isEqualTo(ERROR_NONE);
+
+ // Verify that the service entered the active state.
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE &&
+ (received.getServiceType() == SERVICE_TYPE_REARVIEW ||
+ received.getServiceType() ==
+ SERVICE_TYPE_FRONTVIEW)));
+
+ // Send buffers in separate thread.
+ mHandler.post(() -> {
+ List<StateMachine.HalCallback> callbacks = mHalCallbackCaptor.getAllValues();
+ for (int i = 0; i < MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY; i++) {
+ for (var cb : callbacks) {
+ cb.onFrameEvent(i, buffer);
+ }
+ }
+ });
+
+ // Confirm that a buffer is forwarded to both clients.
+ int timeoutInMs = MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY * MAXIMUM_FRAME_INTERVAL_IN_MS;
+ assertThat(spiedRearviewCallback.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+ assertThat(spiedFrontviewCallback.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+
+ // Stop a video stream for the rearview client and verify that the service is stopped
+ // properly.
+ mCarEvsService.stopVideoStream(spiedRearviewCallback);
+ assertThat(spiedRearviewCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+
+ // Stop a video stream for the frontview client and verify that the service is stopped
+ // properly.
+ mCarEvsService.stopVideoStream(spiedFrontviewCallback);
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ assertThat(spiedFrontviewCallback.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_INACTIVE &&
+ (received.getServiceType() == SERVICE_TYPE_REARVIEW ||
+ received.getServiceType() ==
+ SERVICE_TYPE_FRONTVIEW)));
+ }
+
+ @Test
+ public void testFourConcurrentStreamsFromTwoCamerasWithMultipleServiceConfigurations()
+ throws Exception {
+ HardwareBuffer buffer =
+ HardwareBuffer.create(
+ /* width= */ 64,
+ /* height= */ 32,
+ /* format= */ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ EvsStreamCallbackImpl spiedRearviewCallback0 = spy(new EvsStreamCallbackImpl());
+ EvsStreamCallbackImpl spiedRearviewCallback1 = spy(new EvsStreamCallbackImpl());
+ EvsStreamCallbackImpl spiedLeftviewCallback0 = spy(new EvsStreamCallbackImpl());
+ EvsStreamCallbackImpl spiedLeftviewCallback1 = spy(new EvsStreamCallbackImpl());
+ EvsStatusListenerImpl spiedStatusListener = spy(new EvsStatusListenerImpl());
+
+ // Configure the service to be in the inactive state.
+ mCarEvsService.setServiceState(SERVICE_TYPE_REARVIEW, SERVICE_STATE_INACTIVE);
+ mCarEvsService.setServiceState(SERVICE_TYPE_LEFTVIEW, SERVICE_STATE_INACTIVE);
+
+ // Register a status listener and request starting video streams.
+ mCarEvsService.registerStatusListener(spiedStatusListener);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW,
+ /* token= */ null, spiedRearviewCallback0)).isEqualTo(ERROR_NONE);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_LEFTVIEW,
+ /* token= */ null, spiedLeftviewCallback0)).isEqualTo(ERROR_NONE);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_REARVIEW,
+ /* token= */ null, spiedRearviewCallback1)).isEqualTo(ERROR_NONE);
+ assertThat(mCarEvsService.startVideoStream(SERVICE_TYPE_LEFTVIEW,
+ /* token= */ null, spiedLeftviewCallback1)).isEqualTo(ERROR_NONE);
+
+ // Verify that the service entered the active state.
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE &&
+ (received.getServiceType() == SERVICE_TYPE_REARVIEW ||
+ received.getServiceType() == SERVICE_TYPE_FRONTVIEW)));
+
+ // Send buffers in separate thread.
+ mHandler.post(() -> {
+ List<StateMachine.HalCallback> callbacks = mHalCallbackCaptor.getAllValues();
+ for (int i = 0; i < MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY; i++) {
+ for (var cb : callbacks) {
+ cb.onFrameEvent(i, buffer);
+ }
+ }
+ });
+
+ // Confirm that a buffer is forwarded to all four clients.
+ int timeoutInMs = MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY * MAXIMUM_FRAME_INTERVAL_IN_MS;
+ assertThat(spiedRearviewCallback0.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+ assertThat(spiedLeftviewCallback0.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+ assertThat(spiedRearviewCallback1.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+ assertThat(spiedLeftviewCallback1.waitForFrames(
+ /* expected= */ MINIMUM_NUMBER_OF_FRAMES_TO_VERIFY, timeoutInMs)).isTrue();
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_ACTIVE &&
+ (received.getServiceType() == SERVICE_TYPE_REARVIEW ||
+ received.getServiceType() == SERVICE_TYPE_LEFTVIEW)));
+
+ // Stop a video stream for the first clients and verify that the services still in the
+ // active state.
+ mCarEvsService.stopVideoStream(spiedRearviewCallback0);
+ assertThat(spiedRearviewCallback0.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ mCarEvsService.stopVideoStream(spiedLeftviewCallback0);
+ assertThat(spiedLeftviewCallback0.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isFalse();
+
+ // Stop a video stream for the second clients and verify that the services entered the
+ // inactive state.
+ mCarEvsService.stopVideoStream(spiedRearviewCallback1);
+ assertThat(spiedRearviewCallback1.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ mCarEvsService.stopVideoStream(spiedLeftviewCallback1);
+ assertThat(spiedLeftviewCallback1.waitForEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
+ .isTrue();
+ assertThat(spiedStatusListener.waitFor(SERVICE_STATE_INACTIVE)).isTrue();
+ verify(spiedStatusListener, times(2)).onStatusChanged(argThat(
+ received -> received.getState() == CarEvsManager.SERVICE_STATE_INACTIVE &&
+ (received.getServiceType() == SERVICE_TYPE_REARVIEW ||
+ received.getServiceType() ==
+ SERVICE_TYPE_LEFTVIEW)));
}
private void mockEvsHalService() throws Exception {
@@ -909,18 +1413,6 @@
when(mMockEvsHalWrapper.openCamera(any())).thenReturn(true);
when(mMockEvsHalWrapper.connectToHalServiceIfNecessary()).thenReturn(true);
when(mMockEvsHalWrapper.requestToStartVideoStream()).thenReturn(true);
-
- // Create a buffer to circulate
- HardwareBuffer buffer =
- HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
- /* format= */ HardwareBuffer.RGBA_8888,
- /* layers= */ 1,
- /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
- doAnswer(args -> {
- mCarEvsService.returnFrameBuffer(
- new CarEvsBufferDescriptor(args.getArgument(0), buffer));
- return true;
- }).when(mMockEvsHalWrapper).doneWithFrame(anyInt());
}
/**
@@ -928,9 +1420,42 @@
* {@link android.car.evs.CarEvsManager.CarEvsStatusListener}.
*/
private final static class EvsStatusListenerImpl extends ICarEvsStatusListener.Stub {
+ private final Semaphore mSemaphore = new Semaphore(0);
+
+ private CarEvsStatus mLastUpdate;
+
@Override
public void onStatusChanged(CarEvsStatus status) {
Log.i(TAG, "Received a status change notification: " + status.getState());
+ mLastUpdate = status;
+ mSemaphore.release();
+ }
+
+ public boolean waitFor() {
+ return waitFor(SERVICE_TYPE_ANY, EVENT_TYPE_ANY, DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ public boolean waitFor(int expected) {
+ return waitFor(SERVICE_TYPE_ANY, expected, DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ public boolean waitFor(@CarEvsServiceType int type, int expected) {
+ return waitFor(type, expected, DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ public boolean waitFor(@CarEvsServiceType int type, int expected, int timeout) {
+ try {
+ while (true) {
+ JavaMockitoHelper.await(mSemaphore, timeout);
+ if(expected == mLastUpdate.getState() &&
+ (type == SERVICE_TYPE_ANY || type == mLastUpdate.getServiceType())) {
+ return true;
+ }
+ }
+ } catch (IllegalStateException | InterruptedException e) {
+ Log.d(TAG, "Failure to wait for status update, " + e);
+ return false;
+ }
}
}
@@ -939,15 +1464,59 @@
* {@link android.hardware.automotive.evs.IEvsCameraStream}.
*/
private final static class EvsStreamCallbackImpl extends ICarEvsStreamCallback.Stub {
+ private final Semaphore mFrameSemaphore = new Semaphore(0);
+ private final Semaphore mEventSemaphore = new Semaphore(0);
+
+ private int mLastEvent = CarEvsManager.STREAM_EVENT_NONE;
+
@Override
public void onStreamEvent(@CarEvsStreamEvent int event) {
- Log.i(TAG, "Received stream event " + event);
+ Log.i(TAG, "Received stream event 0x" + Integer.toHexString(event));
+ mLastEvent = event & DATA_MASK;
+ mEventSemaphore.release();
}
@Override
public void onNewFrame(CarEvsBufferDescriptor buffer) {
// Return a buffer immediately
- Log.i(TAG_EVS, "Received buffer " + buffer.getId());
+ Log.i(TAG, "Received buffer 0x" + Integer.toHexString(buffer.getId()));
+ mFrameSemaphore.release();
+ }
+
+ public boolean waitForEvent(int expected) {
+ return waitForEvent(expected, DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ public boolean waitForEvent(int expected, int timeout) {
+ try {
+ while (true) {
+ JavaMockitoHelper.await(mEventSemaphore, timeout);
+ if (expected == mLastEvent) {
+ return true;
+ }
+ }
+ } catch (IllegalStateException | InterruptedException e) {
+ Log.d(TAG, "Failure to wait for an event " + expected);
+ return false;
+ }
+ }
+
+ public boolean waitForFrames(int expected) {
+ return waitForFrames(expected, DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ public boolean waitForFrames(int expected, int timeout) {
+ try {
+ while (expected > 0) {
+ JavaMockitoHelper.await(mFrameSemaphore, timeout);
+ expected -= 1;
+ }
+ } catch (IllegalStateException | InterruptedException e) {
+ Log.d(TAG, "Failure to wait for " + expected + " frames.");
+ return false;
+ }
+
+ return true;
}
}
}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/HalPropValueTest.java b/tests/carservice_unit_test/src/com/android/car/hal/HalPropValueTest.java
index cee58e5..f3b788d 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/HalPropValueTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/HalPropValueTest.java
@@ -18,8 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertThrows;
-
import android.car.hardware.CarPropertyValue;
import android.hardware.automotive.vehicle.RawPropValues;
import android.hardware.automotive.vehicle.VehiclePropConfig;
@@ -875,7 +873,11 @@
android.hardware.automotive.vehicle.VehiclePropValue aidlValue =
new android.hardware.automotive.vehicle.VehiclePropValue();
aidlValue.prop = TEST_BOOL_PROP;
+ aidlValue.areaId = TEST_AREA_ID;
aidlValue.status = status;
+ aidlValue.timestamp = TEST_TIMESTAMP;
+ aidlValue.value = new RawPropValues();
+ aidlValue.value.int32Values = new int[] {1};
return new HalPropValueBuilder(/*isAidl=*/true).build(aidlValue);
}
@@ -883,16 +885,20 @@
public void testToCarPropertyValueUnavailableStatus() {
HalPropValue value = createTestHalPropValueWithStatus(VehiclePropertyStatus.UNAVAILABLE);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, TEST_AREA_ID,
+ CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, Boolean.TRUE));
}
@Test
public void testToCarPropertyValueInternalErrorStatus() {
HalPropValue value = createTestHalPropValueWithStatus(VehiclePropertyStatus.ERROR);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, TEST_AREA_ID, CarPropertyValue.STATUS_ERROR,
+ TEST_TIMESTAMP, Boolean.TRUE));
}
// Creates an empty HalPropValue that does not have any value.
@@ -908,32 +914,40 @@
public void testToCarPropertyValueInvalidBoolProp() {
HalPropValue value = createTestHalPropValueWithNoValue(TEST_BOOL_PROP);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Boolean.FALSE));
}
@Test
public void testToCarPropertyValueInvalidInt32Prop() {
HalPropValue value = createTestHalPropValueWithNoValue(TEST_INT32_PROP);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Integer.valueOf(0)));
}
@Test
public void testToCarPropertyValueInvalidInt64Prop() {
HalPropValue value = createTestHalPropValueWithNoValue(TEST_INT64_PROP);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Long.valueOf(0)));
}
@Test
public void testToCarPropertyValueInvalidFloatProp() {
HalPropValue value = createTestHalPropValueWithNoValue(TEST_FLOAT_PROP);
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(new VehiclePropConfig())));
+ assertThat(value.toCarPropertyValue(TEST_MGR_PROP,
+ new AidlHalPropConfig(new VehiclePropConfig()))).isEqualTo(
+ new CarPropertyValue<>(TEST_MGR_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Float.valueOf(0)));
}
// For int32_vec property, an empty array is considered a valid return value.
@@ -993,10 +1007,10 @@
// We only care about the second element, which indicates containing boolean value.
config.configArray = new int[] {1, 1, 1, 0, 1, 0, 1, 0, 1};
- // Because the mixed property contains boolean value, it must have at least one int array
- // element.
- assertThrows(IllegalStateException.class, () -> value.toCarPropertyValue(
- TEST_MGR_PROP, new AidlHalPropConfig(config)));
+ CarPropertyValue<Object[]> carPropertyValue = value.toCarPropertyValue(
+ TEST_MGR_PROP, new AidlHalPropConfig(config));
+ assertThat(carPropertyValue.getPropertyId()).isEqualTo(TEST_MGR_PROP);
+ assertThat(carPropertyValue.getStatus()).isEqualTo(CarPropertyValue.STATUS_ERROR);
}
@Test
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
index 8e61c4d..1aa68f1 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
@@ -16,10 +16,15 @@
package com.android.car.hal;
+import static android.car.Car.PERMISSION_VENDOR_EXTENSION;
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
import static android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED;
import static android.car.hardware.property.VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE;
+import static android.hardware.automotive.vehicle.VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION;
import static com.android.car.internal.property.CarPropertyHelper.STATUS_OK;
@@ -50,9 +55,9 @@
import android.hardware.automotive.vehicle.VehicleProperty;
import android.hardware.automotive.vehicle.VehiclePropertyChangeMode;
import android.hardware.automotive.vehicle.VehiclePropertyStatus;
+import android.hardware.automotive.vehicle.VehicleVendorPermission;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceSpecificException;
import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
@@ -68,8 +73,6 @@
import com.android.car.internal.property.GetSetValueResultList;
import com.android.car.internal.property.IAsyncPropertyResultCallback;
-import com.google.common.collect.ImmutableList;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -122,6 +125,9 @@
private static final int RECEIVED_REQUEST_ID_3 = 2;
private static final int INT32_PROP = VehiclePropertyIds.INFO_FUEL_DOOR_LOCATION;
private static final int VENDOR_ERROR_CODE = 1234;
+ private static final int VENDOR_PROPERTY_1 = 0x21e01111;
+ private static final int VENDOR_PROPERTY_2 = 0x21e01112;
+ private static final int VENDOR_PROPERTY_3 = 0x21e01113;
private static final AsyncPropertyServiceRequest GET_PROPERTY_SERVICE_REQUEST_1 =
new AsyncPropertyServiceRequest(REQUEST_ID_1, HVAC_TEMPERATURE_SET, /* areaId= */ 0);
private static final AsyncPropertyServiceRequest GET_PROPERTY_SERVICE_REQUEST_2 =
@@ -168,7 +174,7 @@
when(mockPropConfig2.getChangeMode()).thenReturn(VehiclePropertyChangeMode.CONTINUOUS);
when(mockPropConfig2.getMinSampleRate()).thenReturn(20.0f);
when(mockPropConfig2.getMaxSampleRate()).thenReturn(100.0f);
- mPropertyHalService.takeProperties(ImmutableList.of(mockPropConfig1, mockPropConfig2));
+ mPropertyHalService.takeProperties(List.of(mockPropConfig1, mockPropConfig2));
}
@After
@@ -1680,7 +1686,7 @@
@Test
public void isDisplayUnitsProperty_returnsTrueForAllDisplayUnitProperties() {
- for (int propId : ImmutableList.of(VehiclePropertyIds.DISTANCE_DISPLAY_UNITS,
+ for (int propId : List.of(VehiclePropertyIds.DISTANCE_DISPLAY_UNITS,
VehiclePropertyIds.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME,
VehiclePropertyIds.FUEL_VOLUME_DISPLAY_UNITS,
VehiclePropertyIds.TIRE_PRESSURE_DISPLAY_UNITS,
@@ -1701,7 +1707,7 @@
when(mockCarPropertyValue.getAreaId()).thenReturn(0);
when(mockCarPropertyValue.getValue()).thenReturn(1.0f);
when(mockPropConfig.getPropId()).thenReturn(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
- mPropertyHalService.takeProperties(ImmutableList.of(mockPropConfig));
+ mPropertyHalService.takeProperties(List.of(mockPropConfig));
mPropertyHalService.setProperty(mockCarPropertyValue);
@@ -1850,28 +1856,25 @@
public void testGetPropertySyncErrorPropStatus() throws Exception {
HalPropValue value = mPropValueBuilder.build(
AidlVehiclePropValueBuilder.newBuilder(INT32_PROP)
- .setStatus(VehiclePropertyStatus.ERROR).build());
+ .setStatus(VehiclePropertyStatus.ERROR).addIntValues(0).build());
when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
- // If the property has ERROR status, getProperty will throw ServiceSpecificException with
- // STATUS_INTERNAL_ERROR as error code.
- ServiceSpecificException e = assertThrows(ServiceSpecificException.class,
- () -> mPropertyHalService.getProperty(INT32_PROP, /* areaId= */ 0));
- assertThat(e.errorCode).isEqualTo(STATUS_INTERNAL_ERROR);
+ assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
+ new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Integer.valueOf(0)));
}
@Test
public void testGetPropertySyncUnavailablePropStatus() throws Exception {
HalPropValue value = mPropValueBuilder.build(
AidlVehiclePropValueBuilder.newBuilder(INT32_PROP)
- .setStatus(VehiclePropertyStatus.UNAVAILABLE).build());
+ .setStatus(VehiclePropertyStatus.UNAVAILABLE).addIntValues(0).build());
when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
- // If the property has UNAVAILABLE status, getProperty will throw ServiceSpecificException
- // with STATUS_NOT_AVAILABLE as error code.
- ServiceSpecificException e = assertThrows(ServiceSpecificException.class,
- () -> mPropertyHalService.getProperty(INT32_PROP, /* areaId= */ 0));
- assertThat(e.errorCode).isEqualTo(STATUS_NOT_AVAILABLE);
+ assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
+ new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_UNAVAILABLE, /*timestampNanos=*/0,
+ Integer.valueOf(0)));
}
@Test
@@ -1881,11 +1884,9 @@
AidlVehiclePropValueBuilder.newBuilder(INT32_PROP).build());
when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
- // If the property value is not valid and cannot be converted to CarPropertyValue,
- // getProperty will throw ServiceSpecificException with STATUS_INTERNAL_ERROR.
- ServiceSpecificException e = assertThrows(ServiceSpecificException.class,
- () -> mPropertyHalService.getProperty(INT32_PROP, /* areaId= */ 0));
- assertThat(e.errorCode).isEqualTo(STATUS_INTERNAL_ERROR);
+ assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
+ new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
+ CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Integer.valueOf(0)));
}
@Test
@@ -1910,4 +1911,59 @@
verify(mPropertyHalListener).onPropertySetError(PERF_VEHICLE_SPEED, 0,
CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
}
+
+ @Test
+ public void testNoCustomizeVendorPermission() throws Exception {
+ HalPropConfig vendor1Config = mock(HalPropConfig.class);
+ when(vendor1Config.getPropId()).thenReturn(VENDOR_PROPERTY_1);
+ when(mVehicleHal.getPropConfig(SUPPORT_CUSTOMIZE_VENDOR_PERMISSION)).thenReturn(null);
+
+ mPropertyHalService.takeProperties(List.of(vendor1Config));
+
+ // By default we require PERMISSION_VENDOR_EXTENSION for getting/setting vendor props.
+ assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_1)).isEqualTo(
+ PERMISSION_VENDOR_EXTENSION);
+ assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_1)).isEqualTo(
+ PERMISSION_VENDOR_EXTENSION);
+ }
+
+ @Test
+ public void testCustomizeVendorPermission() throws Exception {
+ HalPropConfig mockVendorPermConfig = mock(HalPropConfig.class);
+ // Use the same test config we used in PropertyHalServiceTest.
+ when(mockVendorPermConfig.getConfigArray()).thenReturn(new int[]{
+ VENDOR_PROPERTY_1,
+ VehicleVendorPermission.PERMISSION_DEFAULT,
+ VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
+ VENDOR_PROPERTY_2,
+ VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_ENGINE,
+ VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_ENGINE,
+ VENDOR_PROPERTY_3,
+ VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_INFO,
+ VehicleVendorPermission.PERMISSION_DEFAULT
+ });
+ when(mVehicleHal.getPropConfig(SUPPORT_CUSTOMIZE_VENDOR_PERMISSION)).thenReturn(
+ mockVendorPermConfig);
+ HalPropConfig vendor1Config = mock(HalPropConfig.class);
+ when(vendor1Config.getPropId()).thenReturn(VENDOR_PROPERTY_1);
+ HalPropConfig vendor2Config = mock(HalPropConfig.class);
+ when(vendor2Config.getPropId()).thenReturn(VENDOR_PROPERTY_2);
+ HalPropConfig vendor3Config = mock(HalPropConfig.class);
+ when(vendor3Config.getPropId()).thenReturn(VENDOR_PROPERTY_3);
+
+ mPropertyHalService.takeProperties(List.of(vendor1Config, vendor2Config,
+ vendor3Config));
+
+ assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_1)).isEqualTo(
+ PERMISSION_VENDOR_EXTENSION);
+ assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_1)).isNull();
+ assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_2)).isEqualTo(
+ PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE);
+ assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_2)).isEqualTo(
+ PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE);
+ assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_3)).isEqualTo(
+ PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
+ assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_3)).isEqualTo(
+ PERMISSION_VENDOR_EXTENSION);
+ }
}
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
index 21a9ba4..709be29 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
@@ -53,6 +53,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.car.CarLocalServices;
+import com.android.car.CarOccupantZoneService;
import com.android.car.CarUxRestrictionsManagerService;
import com.android.car.hal.UserHalService;
import com.android.car.internal.common.CommonConstants.UserLifecycleEventType;
@@ -132,6 +133,9 @@
@Mock
private CarPowerManagementService mCarPowerManagementService;
+ @Mock
+ private CarOccupantZoneService mCarOccupantZoneService;
+
private ServiceLauncherContext mContext;
private CarUserService mCarUserService;
private VendorServiceController mController;
@@ -150,7 +154,8 @@
mContext = new ServiceLauncherContext(ApplicationProvider.getApplicationContext());
mCarUserService = new CarUserService(mContext, mUserHal, mUserManager,
- /* maxRunningUsers= */ 2, mUxRestrictionService, mCarPackageManagerService);
+ /* maxRunningUsers= */ 2, mUxRestrictionService, mCarPackageManagerService,
+ mCarOccupantZoneService);
spyOn(mCarUserService);
CarLocalServices.removeServiceForTest(CarUserService.class);
CarLocalServices.addService(CarUserService.class, mCarUserService);
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
index f04bfe4..d24e5d8 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceInfoTest.java
@@ -28,6 +28,7 @@
@RunWith(JUnit4.class)
public class VendorServiceInfoTest {
+ private static final int MAX_RETRIES = 23;
private static final String SERVICE_NAME = "com.andorid.car/.MyService";
@Test
@@ -54,6 +55,12 @@
}
@Test
+ public void testParse_maxRetriesValueNotANumber() {
+ assertThrows(IllegalArgumentException.class,
+ () -> VendorServiceInfo.parse(SERVICE_NAME + "#maxRetries=seven"));
+ }
+
+ @Test
public void testServiceNameWithDefaults() {
VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME);
@@ -189,9 +196,24 @@
}
@Test
+ public void testGetMaxRetries() {
+ VendorServiceInfo info =
+ VendorServiceInfo.parse(SERVICE_NAME + "#maxRetries=" + MAX_RETRIES);
+
+ assertThat(info.getMaxRetries()).isEqualTo(MAX_RETRIES);
+ }
+
+ @Test
+ public void testGetMaxRetries_defaultMaxRetries() {
+ VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME);
+
+ assertThat(info.getMaxRetries()).isEqualTo(VendorServiceInfo.DEFAULT_MAX_RETRIES);
+ }
+
+ @Test
public void allArgs() {
VendorServiceInfo info = VendorServiceInfo.parse(SERVICE_NAME
- + "#bind=bind,user=foreground,trigger=userUnlocked");
+ + "#bind=bind,user=foreground,trigger=userUnlocked,maxRetries=" + MAX_RETRIES);
assertThat(info.getIntent().getComponent())
.isEqualTo(ComponentName.unflattenFromString(SERVICE_NAME));
@@ -200,6 +222,7 @@
assertThat(info.isSystemUserService()).isFalse();
assertThat(info.shouldStartOnUnlock()).isTrue();
assertThat(info.shouldStartAsap()).isFalse();
+ assertThat(info.getMaxRetries()).isEqualTo(MAX_RETRIES);
}
@Test
@@ -245,4 +268,22 @@
assertThat(result).contains("userScope=SYSTEM");
assertThat(result).contains("trigger=RESUME");
}
+
+ @Test
+ public void testToString_maxRetries() {
+ String result = VendorServiceInfo.parse(SERVICE_NAME + "#maxRetries=" + MAX_RETRIES)
+ .toString();
+
+ assertThat(result).contains("component=" + SERVICE_NAME);
+ assertThat(result).contains("maxRetries=" + MAX_RETRIES);
+ }
+
+ @Test
+ public void testToString_defaultMaxRetries() {
+ String result = VendorServiceInfo.parse(SERVICE_NAME)
+ .toString();
+
+ assertThat(result).contains("component=" + SERVICE_NAME);
+ assertThat(result).doesNotContain("maxRetries=");
+ }
}
diff --git a/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
index cf61965..0661c1f 100644
--- a/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
@@ -31,7 +31,9 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@@ -427,6 +429,88 @@
mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
}
+ // need to test SHUTDOWN_PREPARE -> CANCEL scenario
+ @Test
+ public void testUserManagerOnResumeCallAfterCancel() throws Exception {
+ grantPowerPolicyPermission();
+ mPowerSignalListener.addEventListener(PowerHalService.SET_SHUTDOWN_START);
+ mPowerSignalListener.addEventListener(PowerHalService.SET_SHUTDOWN_CANCELLED);
+ mPowerSignalListener.addEventListener(PowerHalService.SET_ON);
+ assertThat(mService.getCurrentPowerPolicy().getPolicyId())
+ .isEqualTo(SYSTEM_POWER_POLICY_INITIAL_ON);
+ mPowerHal.setCurrentPowerState(
+ new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY));
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START);
+ mPowerSignalListener.waitFor(PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_MS);
+
+ mPowerHal.setCurrentPowerState(
+ new PowerState(VehicleApPowerStateReq.CANCEL_SHUTDOWN, /* param= */ 0));
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_CANCELLED);
+
+ mPowerSignalListener.waitFor(PowerHalService.SET_SHUTDOWN_CANCELLED, WAIT_TIMEOUT_MS);
+
+ verify(mUserService).onSuspend();
+
+ assertThat(mService.getCurrentPowerPolicy().getPolicyId())
+ .isEqualTo(SYSTEM_POWER_POLICY_INITIAL_ON);
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, /* param= */ 0));
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_ON);
+
+ mPowerSignalListener.waitFor(PowerHalService.SET_ON, WAIT_TIMEOUT_MS);
+
+ // onResume is being called after notification to VHAL, so there is possibility that
+ // test will check mock before it is actually called, to avoid failure, timeout used.
+ verify(mUserService, timeout(WAIT_TIMEOUT_LONG_MS)).onResume();
+
+ }
+
+ @Test
+ public void testUserManagerOnResumeCallAfterSuspend() throws Exception {
+ mPowerSignalListener.addEventListener(PowerHalService.SET_DEEP_SLEEP_ENTRY);
+ mPowerSignalListener.addEventListener(PowerHalService.SET_DEEP_SLEEP_EXIT);
+ mPowerSignalListener.addEventListener(PowerHalService.SET_ON);
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY));
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+ mPowerSignalListener.waitFor(PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_MS);
+ verify(mUserService).onSuspend();
+
+ // Send the finished signal
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0));
+ mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
+ mPowerSignalListener.waitFor(PowerHalService.SET_DEEP_SLEEP_EXIT, WAIT_TIMEOUT_MS);
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
+ mPowerSignalListener.waitFor(PowerHalService.SET_ON, WAIT_TIMEOUT_MS);
+
+ // onResume is being called after notification to VHAL, so there is possibility that
+ // test will check mock before it is actually called, to avoid failure, timeout used.
+ verify(mUserService, timeout(WAIT_TIMEOUT_LONG_MS)).onResume();
+
+ // suspend and resume again
+ clearInvocations(mUserService);
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY));
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+ mPowerSignalListener.waitFor(PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_MS);
+
+ verify(mUserService).onSuspend();
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0));
+ mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
+ mPowerSignalListener.waitFor(PowerHalService.SET_DEEP_SLEEP_EXIT, WAIT_TIMEOUT_MS);
+
+ mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
+ mPowerSignalListener.waitFor(PowerHalService.SET_ON, WAIT_TIMEOUT_MS);
+ verify(mUserService, timeout(WAIT_TIMEOUT_LONG_MS)).onResume();
+ }
+
@Test
public void testSleepEntryAndWakeup() throws Exception {
mPowerSignalListener.addEventListener(PowerHalService.SET_DEEP_SLEEP_ENTRY);
diff --git a/tests/carservice_unit_test/src/com/android/car/remoteaccess/CarRemoteAccessServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/remoteaccess/CarRemoteAccessServiceUnitTest.java
index 4c13457..e3ce5fc 100644
--- a/tests/carservice_unit_test/src/com/android/car/remoteaccess/CarRemoteAccessServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/remoteaccess/CarRemoteAccessServiceUnitTest.java
@@ -356,14 +356,52 @@
@Test
public void testCarRemoteAccessServiceInit_retryNotifyApState() throws Exception {
when(mRemoteAccessHalWrapper.notifyApStateChange(anyBoolean(), anyBoolean()))
+ .thenReturn(false).thenReturn(false).thenReturn(true);
+
+ mService.init();
+
+ // This should take about 300ms, so waiting 5s is definitely enough.
+ verify(mRemoteAccessHalWrapper, timeout(WAIT_TIMEOUT_MS).times(3)).notifyApStateChange(
+ anyBoolean(), anyBoolean());
+ }
+
+ @Test
+ public void testCarRemoteAccessServiceInit_maxRetryNotifyApState() throws Exception {
+ when(mRemoteAccessHalWrapper.notifyApStateChange(anyBoolean(), anyBoolean()))
.thenReturn(false);
mService.init();
- verify(mRemoteAccessHalWrapper, timeout(1500).times(10)).notifyApStateChange(
+ verify(mRemoteAccessHalWrapper, timeout(WAIT_TIMEOUT_MS).times(10)).notifyApStateChange(
+ /* isReadyForRemoteTask= */ true, /* isWakeupRequired= */ false);
+
+ SystemClock.sleep(100);
+
+ // Verify no more retry.
+ verify(mRemoteAccessHalWrapper, times(10)).notifyApStateChange(
/* isReadyForRemoteTask= */ true, /* isWakeupRequired= */ false);
}
+ @Test
+ public void testCarRemoteAccessServiceInit_resetRetryCountAfterSuccess() throws Exception {
+ when(mRemoteAccessHalWrapper.notifyApStateChange(anyBoolean(), anyBoolean()))
+ .thenReturn(false).thenReturn(false).thenReturn(true);
+
+ mService.init();
+
+ verify(mRemoteAccessHalWrapper, timeout(WAIT_TIMEOUT_MS).times(3)).notifyApStateChange(
+ /* isReadyForRemoteTask= */ true, /* isWakeupRequired= */ false);
+
+ ICarPowerStateListener powerStateListener = getCarPowerStateListener();
+ // Success notifying should not be limited retry count and should reset retry count to 0.
+ for (int i = 0; i < 10; i++) {
+ powerStateListener.onStateChanged(CarPowerManager.STATE_WAIT_FOR_VHAL, 0);
+ }
+
+ // notifyApStateChange is also called when initializaing CarRemoteAccessService.
+ verify(mRemoteAccessHalWrapper, times(13)).notifyApStateChange(
+ /* isReadyForRemoteTask= */ true, /* isWakeupRequired= */ false);
+ }
@Test
public void testAddCarRemoteTaskClient() throws Exception {
@@ -450,6 +488,45 @@
}
@Test
+ public void testRemoveCarRemoteTaskClient_removeActiveTasks() throws Exception {
+ // Only use one package.
+ mockPackageInfo(1);
+ mService.init();
+ mService.setTaskUnbindDelayMs(100);
+ runBootComplete();
+ RemoteAccessHalCallback halCallback = prepareCarRemoteTaskClient();
+
+ String clientId = mRemoteAccessCallback.getClientId();
+ byte[] data = new byte[]{1, 2, 3, 4};
+ // Starts an active task.
+ halCallback.onRemoteTaskRequested(clientId, data);
+
+ PollingCheck.check("onRemoteTaskRequested should be called", WAIT_TIMEOUT_MS,
+ () -> mRemoteAccessCallback.getTaskId() != null);
+ String taskId = mRemoteAccessCallback.getTaskId();
+
+ // This should clear the active tasks, after 100ms, the client should be unbound and the
+ // device should be shutdown.
+ mService.removeCarRemoteTaskClient(mRemoteAccessCallback);
+
+ // This should throw exception because clientId is not valid.
+ assertThrows(IllegalArgumentException.class, () -> mService.reportRemoteTaskDone(
+ clientId, taskId));
+
+ mService.addCarRemoteTaskClient(mRemoteAccessCallback);
+
+ // This should throw exception because the task was cleared so the task ID is not valid.
+ assertThrows(IllegalArgumentException.class, () -> mService.reportRemoteTaskDone(
+ clientId, taskId));
+
+ // The client must be unbound.
+ verify(mContext, timeout(WAIT_TIMEOUT_MS)).unbindService(any());
+ // The device must be shutdown.
+ verify(mCarPowerManagementService, timeout(WAIT_TIMEOUT_MS)).requestShutdownAp(
+ anyInt(), anyBoolean());
+ }
+
+ @Test
public void testRemoteTaskRequested() throws Exception {
mService.init();
runBootComplete();
diff --git a/tests/carservice_unit_test/src/com/android/car/user/BaseCarUserServiceTestCase.java b/tests/carservice_unit_test/src/com/android/car/user/BaseCarUserServiceTestCase.java
index 6436a2a..a1d1c84 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/BaseCarUserServiceTestCase.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/BaseCarUserServiceTestCase.java
@@ -628,7 +628,8 @@
mInitialUserSetter,
mCarUxRestrictionService,
mHandler,
- mCarPackageManagerService);
+ mCarPackageManagerService,
+ mCarOccupantZoneService);
}
/**
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
index 7a6834b..5888e90 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
@@ -490,7 +490,6 @@
// Arrange.
mockUmGetVisibleUsers(mMockedUserManager);
when(mMockedUserManager.isUserRunning(UserHandle.of(TEST_USER_ID))).thenReturn(true);
- mockContextCreateContextAsUser(mMockContext, mMockUserContext, TEST_USER_ID);
mockCarServiceHelperGetMainDisplayAssignedToUser(TEST_USER_ID, TEST_DISPLAY_ID);
mockUmIsVisibleBackgroundUsersSupported(mMockedUserManager, true);
@@ -499,7 +498,7 @@
sendUserVisibleEvent(TEST_USER_ID);
// Verify.
- verify(mMockUserContext).startService(any());
+ verify(mMockContext).bindServiceAsUser(any(), any(), anyInt(), any());
}
@Test
@@ -508,7 +507,6 @@
// Arrange.
mockUmGetVisibleUsers(mMockedUserManager);
when(mMockedUserManager.isUserRunning(UserHandle.of(TEST_USER_ID))).thenReturn(true);
- mockContextCreateContextAsUser(mMockContext, mMockUserContext, TEST_USER_ID);
mockCarServiceHelperGetMainDisplayAssignedToUser(TEST_USER_ID, Display.DEFAULT_DISPLAY);
mockUmIsVisibleBackgroundUsersSupported(mMockedUserManager, true);
mockUmIsVisibleBackgroundUsersOnDefaultDisplaySupported(mMockedUserManager, true);
@@ -518,7 +516,7 @@
sendUserVisibleEvent(TEST_USER_ID);
// Verify.
- verify(mMockUserContext, never()).startService(any());
+ verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(), any());
}
@Test
diff --git a/tools/GenericCarApiBuilder/GenericCarApiBuilder.jar b/tools/GenericCarApiBuilder/GenericCarApiBuilder.jar
index fdf3f25..3816422 100644
--- a/tools/GenericCarApiBuilder/GenericCarApiBuilder.jar
+++ b/tools/GenericCarApiBuilder/GenericCarApiBuilder.jar
Binary files differ
diff --git a/tools/GenericCarApiBuilder/src/com/android/car/tool/apibuilder/GenerateAPI.java b/tools/GenericCarApiBuilder/src/com/android/car/tool/apibuilder/GenerateAPI.java
index f9a9b33..fb667ad 100644
--- a/tools/GenericCarApiBuilder/src/com/android/car/tool/apibuilder/GenerateAPI.java
+++ b/tools/GenericCarApiBuilder/src/com/android/car/tool/apibuilder/GenerateAPI.java
@@ -293,6 +293,7 @@
}
private static void write(String filePath, List<String> data) throws IOException {
+ Collections.sort(data);
Path file = Paths.get(filePath);
Files.write(file, data, StandardCharsets.UTF_8);
}