java.lang.Object
↳ContextWrapper
↳androidx.car.app.CarContext
Subclasses:
TestCarContext
Gradle dependencies
compile group: 'androidx.car.app', name: 'app', version: '1.7.0-beta01'
- groupId: androidx.car.app
- artifactId: app
- version: 1.7.0-beta01
Artifact androidx.car.app:app:1.7.0-beta01 it located at Google repository (https://maven.google.com/)
Overview
The CarContext class is a  subclass accessible to your CarAppService and Screen instances, which provides access to car services such as the
 ScreenManager for managing the screen stack, the AppManager for general
 app-related functionality such as accessing a surface for drawing your navigation app's map, and
 the NavigationManager used by turn-by-turn navigation apps to communicate navigation
 metadata and other navigation-related events with the host. See Access the navigation templates
 for a comprehensive list of library functionality available to navigation apps.
 
Whenever you use a CarContext to load resources, the following configuration elements come
 from the car screen's configuration, and not the phone:
 
 Please refer here, on how
 to use configuration qualifiers in your resources.
Summary
| Fields | 
|---|
| public static final java.lang.String | ACTION_NAVIGATE Standard action for navigating to a location. | 
| public static final java.lang.String | APP_SERVICE Manages all app events such as invalidating the UI, showing a toast, etc. | 
| public static final java.lang.String | CAR_SERVICE Internal usage only. | 
| public static final java.lang.String | CONSTRAINT_SERVICE Manages constraints for the app as enforced by the connected host. | 
| public static final java.lang.String | EXTRA_START_CAR_APP_BINDER_KEY Key for including a IStartCarApp in the notification , for starting the app
 if it has not been opened yet. | 
| public static final java.lang.String | HARDWARE_SERVICE Manages access to androidx.car.app.hardware properties, sensors and actions. | 
| public static final java.lang.String | MEDIA_PLAYBACK_SERVICE Manages the media requests from 3p apps such as providing a media session token, | 
| public static final java.lang.String | NAVIGATION_SERVICE Manages all navigation events such as starting navigation when focus is granted, abandoning
 navigation when focus is lost, etc. | 
| public static final java.lang.String | SCREEN_SERVICE Manages the screens of the app, including the screen stack. | 
| public static final java.lang.String | SUGGESTION_SERVICE Manages posting suggestion events | 
| Methods | 
|---|
| public static CarContext | create(Lifecycle lifecycle) 
 | 
| public void | finishCarApp() 
 Requests to finish the car app. | 
| public ComponentName | getCallingComponent() 
 Return the component (service or activity) that invoked this car app. | 
| public int | getCarAppApiLevel() 
 Retrieves the API level negotiated with the host. | 
| public java.lang.Object | getCarService(java.lang.Class<java.lang.Object> serviceClass) 
 Returns a car service by class. | 
| public java.lang.Object | getCarService(java.lang.String name) 
 Provides a car service by name. | 
| public java.lang.String | getCarServiceName(java.lang.Class<java.lang.Object> serviceClass) 
 Gets the name of the car service that is represented by the specified class. | 
| public HostInfo | getHostInfo() 
 Returns information about the host attached to this service. | 
| public OnBackPressedDispatcher | getOnBackPressedDispatcher() 
 Returns the OnBackPressedDispatcher that will be triggered when the user clicks a
 back button. | 
| public boolean | isDarkMode() 
 Returns true if the car is set to dark mode. | 
| public void | requestPermissions(java.util.List<java.lang.String> permissions, java.util.concurrent.Executor executor, OnRequestPermissionsListener listener) 
 Requests the provided permissions from the user. | 
| public void | requestPermissions(java.util.List<java.lang.String> permissions, OnRequestPermissionsListener listener) 
 Requests the provided permissions from the user, calling the provided listener in the main thread. | 
| public void | setCarAppResult(int resultCode, Intent data) 
 Sets the result of this car app. | 
| public void | setCarHost(ICarHost carHost) 
 | 
| public void | startCarApp(Intent intent) 
 Starts a car app on the car screen. | 
| public static void | startCarApp(Intent notificationIntent, Intent appIntent) 
 Starts the car app on the car screen. | 
| public void | updateHandshakeInfo(HandshakeInfo handshakeInfo) 
 Updates context information based on the information provided during connection handshake | 
| from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait | 
Fields
public static final java.lang.String 
APP_SERVICEManages all app events such as invalidating the UI, showing a toast, etc.
public static final java.lang.String 
NAVIGATION_SERVICEManages all navigation events such as starting navigation when focus is granted, abandoning
 navigation when focus is lost, etc.
public static final java.lang.String 
SCREEN_SERVICEManages the screens of the app, including the screen stack.
public static final java.lang.String 
CONSTRAINT_SERVICEManages constraints for the app as enforced by the connected host.
public static final java.lang.String 
CAR_SERVICEInternal usage only. Top level binder to host.
public static final java.lang.String 
HARDWARE_SERVICEManages access to androidx.car.app.hardware properties, sensors and actions.
public static final java.lang.String 
SUGGESTION_SERVICEManages posting suggestion events
public static final java.lang.String 
MEDIA_PLAYBACK_SERVICEManages the media requests from 3p apps such as providing a media session token,
public static final java.lang.String 
EXTRA_START_CAR_APP_BINDER_KEYKey for including a IStartCarApp in the notification , for starting the app
 if it has not been opened yet.
public static final java.lang.String 
ACTION_NAVIGATEStandard action for navigating to a location.
 
Used as the 's action for starting a navigation via
 CarContext.startCarApp(Intent).
Constructors
Methods
public java.lang.Object 
getCarService(java.lang.String name)
Provides a car service by name.
 
The class of the returned object varies by the requested name.
 
Currently supported car services, and their respective classes, are:
 
   - CarContext.APP_SERVICE
   
- An AppManager for communication between the app and the host.
   
- CarContext.NAVIGATION_SERVICE
   
- A NavigationManager for management of navigation updates.
   
- CarContext.SCREEN_SERVICE
   
- A ScreenManager for management of Screens.
   
- CarContext.CONSTRAINT_SERVICE
   
- A ConstraintManager for management of content limits.
   
- CarContext.HARDWARE_SERVICE
   
- A CarHardwareManager for interacting with car hardware (e.g. sensors) data.
   
- CarContext.SUGGESTION_SERVICE
   
- A SuggestionManager for posting
   Suggestions.
 
This method should not be called until the  of the context's
 Session is at least .
Parameters:
name: The name of the car service requested. This should be one of
             CarContext.APP_SERVICE,
             CarContext.NAVIGATION_SERVICE or CarContext.SCREEN_SERVICE
Returns:
The car service instance
public java.lang.Object 
getCarService(java.lang.Class<java.lang.Object> serviceClass)
Returns a car service by class.
 
See CarContext.getCarService(String) for a list of the supported car services.
 
This method should not be called until the  of the context's
 Session is at least .
Parameters:
serviceClass: the class of the requested service
public java.lang.String 
getCarServiceName(java.lang.Class<java.lang.Object> serviceClass)
Gets the name of the car service that is represented by the specified class.
 
This method should not be called until the  of the context's
 Session is at least .
Parameters:
serviceClass: the class of the requested service
Returns:
the car service name to use with CarContext.getCarService(String)
See also: CarContext.getCarService(String)
public void 
startCarApp(Intent intent)
Starts a car app on the car screen.
 
The target application will get the  via Session.onCreateScreen(Intent)
 or Session.onNewIntent(Intent).
 
Supported s:
 
   - An  to navigate.
   
- The action must be CarContext.ACTION_NAVIGATE.
   - The data URI scheme must be either a latitude,longitude pair, or a + separated string
       query as follows:
   - 1) "geo:12.345,14.8767" for a latitude, longitude pair.
   - 2) "geo:0,0?q=123+Main+St,+Seattle,+WA+98101" for an address.
   - 3) "geo:0,0?q=a+place+name" for a place to search for.
   
- An  to make a phone call.
   
- The  must be created as defined here.
   
- An  to start this app in the car.
   
- The component name of the intent must be the one for the CarAppService that
       contains this CarContext. If the component name is for a different
       component, the method will throw a java.lang.SecurityException.
This method should not be called until the  of the context's
 Session is at least .
Parameters:
intent: the  to send to the target application
public static void 
startCarApp(Intent notificationIntent, Intent appIntent)
Deprecated: use CarPendingIntent.getCarApp(Context, int, Intent, int) to create
 the pending intent for the notification action.  This API will NOT work for Automotive OS.
Starts the car app on the car screen.
 
Use this method if the app has received a broadcast due to a notification action.
Parameters:
notificationIntent: the  that the app received via broadcast due to a
                           user taking an action on a notification in the car
appIntent: the  to use for starting the car app. See CarContext.startCarApp(Intent) for the documentation on valid
                           s
public void 
finishCarApp()
Requests to finish the car app.
 
Call this when your app is done and should be closed. The Session corresponding
 to this CarContext will become State.DESTROYED.
 
At some point after this call, the OS will destroy your CarAppService.
 
This method should not be called until the  of the context's
 Session is at least .
public void 
setCarAppResult(int resultCode, Intent data)
Sets the result of this car app.
 
In Android Automotive OS, this is equivalent to .
 Call this to set the result that your CarAppActivity will return to its caller.
 
This method is not implemented in Android Auto.
Parameters:
resultCode: the result code to propagate back to the originating app, often
                    or 
data: the data to propagate back to the originating app
public ComponentName 
getCallingComponent()
Return the component (service or activity) that invoked this car app.
 
This is who the data in CarContext.setCarAppResult(int, Intent) will be sent to. You can
 use this information to validate that the recipient is allowed to receive the data.
 
Starting applications for result is not implemented in Android Auto, and this method
 will always return null for that platform.
Returns:
the  of the component that will receive your reply, or
 null if none
public boolean 
isDarkMode()
Returns true if the car is set to dark mode.
 
Navigation applications must redraw their map with the proper dark colors when the host
 determines that conditions warrant it, as signaled by the value returned by this method.
 
Whenever the dark mode status changes, you will receive a call to Session.onCarConfigurationChanged(Configuration).
 
This method should not be called until the  of the context's
 Session is at least .
Returns the OnBackPressedDispatcher that will be triggered when the user clicks a
 back button.
 
The default back press behavior is to call ScreenManager.pop().
 
To override the default behavior, register a
 OnBackPressedCallback via calling
 OnBackPressedDispatcher.addCallback(LifecycleOwner, OnBackPressedCallback). Using
 the LifecycleOwner ensures that your callback is only registered while its
 Lifecycle is at least .
 
If there is a OnBackPressedCallback that is added and
 enabled, and you'd like to remove the top Screen as a part of the callback, you
 MUST call ScreenManager.pop() in the callback. The default behavior is
 overridden when you have a callback enabled.
public int 
getCarAppApiLevel()
Retrieves the API level negotiated with the host.
 
API levels are used during client and host connection handshake to negotiate a common set
 of elements that both processes can understand. Different devices might have different host
 versions. Each of these hosts will support a range of API levels, as a way to provide
 backwards compatibility.
 
Applications can also provide forward compatibility, by declaring support for a
 AppInfo.getMinCarAppApiLevel() lower than AppInfo.getLatestCarAppApiLevel().
 See AppInfo.getMinCarAppApiLevel() for more details.
 
Clients must ensure no elements annotated with a RequiresCarApi value higher
 than returned by this method is used at runtime.
 
Refer to RequiresCarApi description for more details on how to
 implement forward compatibility.
 
This method should not be called until the  of the context's
 Session is at least .
Returns:
a value between AppInfo.getMinCarAppApiLevel() and
 AppInfo.getLatestCarAppApiLevel(). In case of incompatibility, the host will
 disconnect from the service before completing the handshake
Returns information about the host attached to this service.
 
This method should not be called until the  of the context's
 Session is at least .
Returns:
The HostInfo of the connected host, or null if it is not available.
See also: HostInfo
Requests the provided permissions from the user, calling the provided listener in the main thread.
 
The app can define a branded background to the permission request through the
 carPermissionActivityLayout theme attribute, by declaring it in a theme and
 referencing the theme from the androidx.car.app.theme metadata.
 
In AndroidManifest.xml, under the application element
 corresponding to the car app:
 
 
 
 The 
CarAppTheme style is defined as any other themes in a resource file:
 
 
   
 
 
 The default behavior is to have no background behind the permission request.
See also: CarContext.requestPermissions(List, Executor, OnRequestPermissionsListener)
public void 
requestPermissions(java.util.List<java.lang.String> permissions, java.util.concurrent.Executor executor, 
OnRequestPermissionsListener listener)
Requests the provided permissions from the user.
 
When the result is available, the listener provided will be called using the
 java.util.concurrent.Executor provided.
 
This method should be called using a
 ParkedOnlyOnClickListener.
 
If this method is called while the host deems it is unsafe (for example, when the user
 is driving), the permission(s) will not be requested from the user.
 
If the Session is destroyed before the user accepts or rejects the permissions,
 the callback will not be executed.
 
Platform Considerations
 Using this method allows the app to work across all platforms supported by the library with
 the same API (e.g. Android Auto on mobile devices and Android Automotive OS on native car
 heads unit). On a mobile platform, this method will start an activity that will display the
 platform's permissions UI over it. You can choose to not
 use this method and instead implement your own activity and code to request the
 permissions in that platform. On Automotive OS however, distraction-optimized activities
 other than CarAppActivity are not allowed and may be
 rejected during app submission. See CarAppActivity for
 more details.
Parameters:
permissions: the runtime permissions to request from the user
executor: the executor that will be used for calling the callback provided
listener: listener that will be notified when the user takes action on the
                    permission request
public void 
setCarHost(ICarHost carHost)
Updates context information based on the information provided during connection handshake
Source
/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.car.app;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.car.app.utils.LogTags.TAG;
import static java.util.Objects.requireNonNull;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Display;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.DoNotInline;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.constraints.ConstraintManager;
import androidx.car.app.hardware.CarHardwareManager;
import androidx.car.app.managers.ManagerCache;
import androidx.car.app.managers.ResultManager;
import androidx.car.app.media.MediaPlaybackManager;
import androidx.car.app.navigation.NavigationManager;
import androidx.car.app.notification.CarPendingIntent;
import androidx.car.app.suggestion.SuggestionManager;
import androidx.car.app.utils.RemoteUtils;
import androidx.car.app.utils.ThreadUtils;
import androidx.car.app.versioning.CarAppApiLevel;
import androidx.car.app.versioning.CarAppApiLevels;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
/**
 * The CarContext class is a {@link ContextWrapper} subclass accessible to your {@link
 * CarAppService} and {@link Screen} instances, which provides access to car services such as the
 * {@link ScreenManager} for managing the screen stack, the {@link AppManager} for general
 * app-related functionality such as accessing a surface for drawing your navigation app's map, and
 * the {@link NavigationManager} used by turn-by-turn navigation apps to communicate navigation
 * metadata and other navigation-related events with the host. See Access the navigation templates
 * for a comprehensive list of library functionality available to navigation apps.
 *
 * <p>Whenever you use a CarContext to load resources, the following configuration elements come
 * from the car screen's configuration, and not the phone:
 *
 * <ul>
 *   <li>Screen width.
 *   <li>Screen height.
 *   <li>Screen pixel density (DPI).
 *   <li>Night mode (See {@link #isDarkMode}).
 * </ul>
 *
 * <p>Please refer <a
 * href="https://developer.android.com/guide/topics/resources/providing-resources">here</a>, on how
 * to use configuration qualifiers in your resources.
 *
 * @see #getCarService
 */
public class CarContext extends ContextWrapper {
    /**
     * Represents the types of services for client-host communication.
     *
     */
    @SuppressWarnings({
            "UnsafeOptInUsageError"})
    @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE, CONSTRAINT_SERVICE,
            HARDWARE_SERVICE, SUGGESTION_SERVICE, MEDIA_PLAYBACK_SERVICE})
    @Retention(RetentionPolicy.SOURCE)
    @RestrictTo(LIBRARY)
    public @interface CarServiceType {
    }
    /** Manages all app events such as invalidating the UI, showing a toast, etc. */
    public static final String APP_SERVICE = "app";
    /**
     * Manages all navigation events such as starting navigation when focus is granted, abandoning
     * navigation when focus is lost, etc.
     */
    public static final String NAVIGATION_SERVICE = "navigation";
    /** Manages the screens of the app, including the screen stack. */
    public static final String SCREEN_SERVICE = "screen";
    /** Manages constraints for the app as enforced by the connected host. */
    @RequiresCarApi(2)
    public static final String CONSTRAINT_SERVICE = "constraints";
    /**
     * Internal usage only. Top level binder to host.
     */
    public static final String CAR_SERVICE = "car";
    /** Manages access to androidx.car.app.hardware properties, sensors and actions. */
    @RequiresCarApi(3)
    public static final String HARDWARE_SERVICE = "hardware";
    /**
     * Manages posting suggestion events
     */
    public static final String SUGGESTION_SERVICE = "suggestion";
    /**
     * Manages the media requests from 3p apps such as providing a media session token,
     */
    @ExperimentalCarApi
    public static final String MEDIA_PLAYBACK_SERVICE = "media_playback";
    /**
     * Key for including a IStartCarApp in the notification {@link Intent}, for starting the app
     * if it has not been opened yet.
     */
    public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra"
            + ".START_CAR_APP_BINDER_KEY";
    /**
     * Standard action for navigating to a location.
     *
     * <p>Used as the {@link Intent}'s action for starting a navigation via
     * {@link #startCarApp(Intent)}.
     */
    public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
    /**
     * Action for requesting permissions on the car app activity.
     */
    static final String REQUEST_PERMISSIONS_ACTION = "androidx.car.app.action.REQUEST_PERMISSIONS";
    /**
     * Key for string array extra for permissions to request.
     */
    static final String EXTRA_PERMISSIONS_KEY = "androidx.car.app.action.EXTRA_PERMISSIONS_KEY";
    /**
     * Key for binder extra for permission result callback.
     */
    static final String EXTRA_ON_REQUEST_PERMISSIONS_RESULT_LISTENER_KEY =
            "androidx.car.app.action.EXTRA_ON_REQUEST_PERMISSIONS_RESULT_LISTENER_KEY";
    private final OnBackPressedDispatcher mOnBackPressedDispatcher;
    private final HostDispatcher mHostDispatcher;
    private final Lifecycle mLifecycle;
    private final ManagerCache mManagers = new ManagerCache();
    /** API level, updated once host connection handshake is completed. */
    @CarAppApiLevel
    private int mCarAppApiLevel = CarAppApiLevels.UNKNOWN;
    @Nullable
    private HostInfo mHostInfo = null;
    @NonNull
    @RestrictTo(LIBRARY)
    public static CarContext create(@NonNull Lifecycle lifecycle) {
        return new CarContext(lifecycle, new HostDispatcher());
    }
    /**
     * Provides a car service by name.
     *
     * <p>The class of the returned object varies by the requested name.
     *
     * <p>Currently supported car services, and their respective classes, are:
     *
     * <dl>
     *   <dt>{@link #APP_SERVICE}
     *   <dd>An {@link AppManager} for communication between the app and the host.
     *   <dt>{@link #NAVIGATION_SERVICE}
     *   <dd>A {@link NavigationManager} for management of navigation updates.
     *   <dt>{@link #SCREEN_SERVICE}
     *   <dd>A {@link ScreenManager} for management of {@link Screen}s.
     *   <dt>{@link #CONSTRAINT_SERVICE}
     *   <dd>A {@link ConstraintManager} for management of content limits.
     *   <dt>{@link #HARDWARE_SERVICE}
     *   <dd>A {@link CarHardwareManager} for interacting with car hardware (e.g. sensors) data.
     *   <dt>{@link #SUGGESTION_SERVICE}
     *   <dd>A {@link SuggestionManager} for posting
     *   {@link androidx.car.app.suggestion.model.Suggestion}s.
     * </dl>
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @param name The name of the car service requested. This should be one of
     *             {@link #APP_SERVICE},
     *             {@link #NAVIGATION_SERVICE} or {@link #SCREEN_SERVICE}
     * @return The car service instance
     * @throws IllegalArgumentException if {@code name} does not refer to a valid car service
     * @throws IllegalStateException    if the service referred by {@code name} can not be
     *                                  instantiated (e.g. missing library dependency)
     * @throws NullPointerException     if {@code name} is {@code null}
     */
    @NonNull
    public Object getCarService(@CarServiceType @NonNull String name) {
        requireNonNull(name);
        return mManagers.getOrCreate(name);
    }
    /**
     * Returns a car service by class.
     *
     * <p>See {@link #getCarService(String)} for a list of the supported car services.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @param serviceClass the class of the requested service
     * @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
     *                                  service
     * @throws IllegalStateException    if {@code serviceClass} can not be instantiated (e.g.
     *                                  missing library dependency)
     * @throws NullPointerException     if {@code serviceClass} is {@code null}
     */
    @NonNull
    public <T> T getCarService(@NonNull Class<T> serviceClass) {
        requireNonNull(serviceClass);
        return mManagers.getOrCreate(serviceClass);
    }
    /**
     * Gets the name of the car service that is represented by the specified class.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @param serviceClass the class of the requested service
     * @return the car service name to use with {@link #getCarService(String)}
     * @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
     *                                  service
     * @throws NullPointerException     if {@code serviceClass} is {@code null}
     * @see #getCarService
     */
    @NonNull
    @CarServiceType
    public String getCarServiceName(@NonNull Class<?> serviceClass) {
        requireNonNull(serviceClass);
        return mManagers.getName(serviceClass);
    }
    /**
     * Starts a car app on the car screen.
     *
     * <p>The target application will get the {@link Intent} via {@link Session#onCreateScreen}
     * or {@link Session#onNewIntent}.
     *
     * <p>Supported {@link Intent}s:
     *
     * <dl>
     *   <dt>An {@link Intent} to navigate.
     *   <dd>The action must be {@link #ACTION_NAVIGATE}.
     *   <dd>The data URI scheme must be either a latitude,longitude pair, or a + separated string
     *       query as follows:
     *   <dd>1) "geo:12.345,14.8767" for a latitude, longitude pair.
     *   <dd>2) "geo:0,0?q=123+Main+St,+Seattle,+WA+98101" for an address.
     *   <dd>3) "geo:0,0?q=a+place+name" for a place to search for.
     *   <dt>An {@link Intent} to make a phone call.
     *   <dd>The {@link Intent} must be created as defined <a
     *       href="https://developer.android
     *       .com/guide/components/intents-common#DialPhone">here</a>.
     *   <dt>An {@link Intent} to start this app in the car.
     *   <dd>The component name of the intent must be the one for the {@link CarAppService} that
     *       contains this {@link CarContext}. If the component name is for a different
     *       component, the method will throw a {@link SecurityException}.
     * </dl>
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @param intent the {@link Intent} to send to the target application
     * @throws SecurityException    if the app attempts to start a different app explicitly or
     *                              does not have permissions for the requested action
     * @throws HostException        if the remote call fails. For example, if the intent cannot be
     *                              handled by the car host.
     * @throws NullPointerException if {@code intent} is {@code null}
     */
    public void startCarApp(@NonNull Intent intent) {
        requireNonNull(intent);
        mHostDispatcher.dispatch(
                CarContext.CAR_SERVICE,
                "startCarApp", (ICarHost host) -> {
                    host.startCarApp(intent);
                    return null;
                }
        );
    }
    /**
     * Starts the car app on the car screen.
     *
     * <p>Use this method if the app has received a broadcast due to a notification action.
     *
     * @param notificationIntent the {@link Intent} that the app received via broadcast due to a
     *                           user taking an action on a notification in the car
     * @param appIntent          the {@link Intent} to use for starting the car app. See {@link
     *                           #startCarApp(Intent)} for the documentation on valid
     *                           {@link Intent}s
     * @throws InvalidParameterException if {@code notificationIntent} is not an {@link Intent}
     *                                   received from a broadcast, due to an action taken by the
     *                                   user in the car
     * @throws NullPointerException      if either {@code notificationIntent} or {@code appIntent
     *                                   } are {@code null}
     * @deprecated use {@link CarPendingIntent#getCarApp(Context, int, Intent, int)} to create
     * the pending intent for the notification action.  This API will NOT work for Automotive OS.
     */
    @Deprecated
    public static void startCarApp(@NonNull Intent notificationIntent, @NonNull Intent appIntent) {
        requireNonNull(notificationIntent);
        requireNonNull(appIntent);
        IBinder binder = null;
        Bundle extras = notificationIntent.getExtras();
        if (extras != null) {
            binder = extras.getBinder(EXTRA_START_CAR_APP_BINDER_KEY);
        }
        if (binder == null) {
            throw new IllegalArgumentException("Notification intent missing expected extra");
        }
        IStartCarApp startCarAppInterface = requireNonNull(IStartCarApp.Stub.asInterface(binder));
        RemoteUtils.dispatchCallToHost(
                "startCarApp from notification", () -> {
                    startCarAppInterface.startCarApp(appIntent);
                    return null;
                }
        );
    }
    /**
     * Requests to finish the car app.
     *
     * <p>Call this when your app is done and should be closed. The {@link Session} corresponding
     * to this {@link CarContext} will become {@code State.DESTROYED}.
     *
     * <p>At some point after this call, the OS will destroy your {@link CarAppService}.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     */
    public void finishCarApp() {
        mHostDispatcher.dispatch(
                CarContext.CAR_SERVICE,
                "finish", (ICarHost host) -> {
                    host.finish();
                    return null;
                }
        );
    }
    /**
     * Sets the result of this car app.
     *
     * <p>In Android Automotive OS, this is equivalent to {@link Activity#setResult(int, Intent)}.
     * Call this to set the result that your {@code CarAppActivity} will return to its caller.
     *
     * <p><b>This method is not implemented in Android Auto.</b>
     *
     * @param resultCode the result code to propagate back to the originating app, often
     *                   {@link Activity#RESULT_CANCELED} or {@link Activity#RESULT_OK}
     * @param data       the data to propagate back to the originating app
     * @throws IllegalStateException if the method is not supported in the current platform.
     */
    @RequiresCarApi(2)
    public void setCarAppResult(int resultCode, @Nullable Intent data) {
        getCarService(ResultManager.class).setCarAppResult(resultCode, data);
    }
    /**
     * Return the component (service or activity) that invoked this car app.
     *
     * <p>This is who the data in {@link #setCarAppResult(int, Intent)} will be sent to. You can
     * use this information to validate that the recipient is allowed to receive the data.
     *
     * <p><b>Starting applications for result is not implemented in Android Auto, and this method
     * will always return null for that platform.</b>
     *
     * @return the {@link ComponentName} of the component that will receive your reply, or
     * {@code null} if none
     */
    @Nullable
    @RequiresCarApi(2)
    public ComponentName getCallingComponent() {
        try {
            return getCarService(ResultManager.class).getCallingComponent();
        } catch (IllegalStateException ex) {
            // ResultManager is not implemented in the current platform.
            return null;
        }
    }
    /**
     * Returns {@code true} if the car is set to dark mode.
     *
     * <p>Navigation applications must redraw their map with the proper dark colors when the host
     * determines that conditions warrant it, as signaled by the value returned by this method.
     *
     * <p>Whenever the dark mode status changes, you will receive a call to {@link
     * Session#onCarConfigurationChanged}.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     */
    public boolean isDarkMode() {
        return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
                == Configuration.UI_MODE_NIGHT_YES;
    }
    /**
     * Returns the {@link OnBackPressedDispatcher} that will be triggered when the user clicks a
     * back button.
     *
     * <p>The default back press behavior is to call {@link ScreenManager#pop}.
     *
     * <p>To override the default behavior, register a
     * {@link androidx.activity.OnBackPressedCallback} via calling
     * {@link OnBackPressedDispatcher#addCallback(LifecycleOwner, OnBackPressedCallback)}. Using
     * the {@link LifecycleOwner} ensures that your callback is only registered while its
     * {@link Lifecycle} is at least {@link Lifecycle.State#STARTED}.
     *
     * <p>If there is a {@link androidx.activity.OnBackPressedCallback} that is added and
     * enabled, and you'd like to remove the top {@link Screen} as a part of the callback, you
     * <b>MUST</b> call {@link ScreenManager#pop} in the callback. The default behavior is
     * overridden when you have a callback enabled.
     */
    @NonNull
    public OnBackPressedDispatcher getOnBackPressedDispatcher() {
        return mOnBackPressedDispatcher;
    }
    /**
     * Retrieves the API level negotiated with the host.
     *
     * <p>API levels are used during client and host connection handshake to negotiate a common set
     * of elements that both processes can understand. Different devices might have different host
     * versions. Each of these hosts will support a range of API levels, as a way to provide
     * backwards compatibility.
     *
     * <p>Applications can also provide forward compatibility, by declaring support for a
     * {@link AppInfo#getMinCarAppApiLevel()} lower than {@link AppInfo#getLatestCarAppApiLevel()}.
     * See {@link AppInfo#getMinCarAppApiLevel()} for more details.
     *
     * <p>Clients must ensure no elements annotated with a {@link RequiresCarApi} value higher
     * than returned by this method is used at runtime.
     *
     * <p>Refer to {@link RequiresCarApi} description for more details on how to
     * implement forward compatibility.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @return a value between {@link AppInfo#getMinCarAppApiLevel()} and
     * {@link AppInfo#getLatestCarAppApiLevel()}. In case of incompatibility, the host will
     * disconnect from the service before completing the handshake
     * @throws IllegalStateException if invoked before the connection handshake with the host has
     *                               been completed (for example, before
     *                               {@link Session#onCreateScreen(Intent)})
     */
    @CarAppApiLevel
    public int getCarAppApiLevel() {
        if (mCarAppApiLevel == CarAppApiLevels.UNKNOWN) {
            throw new IllegalStateException("Car App API level hasn't been established yet");
        }
        return mCarAppApiLevel;
    }
    /**
     * Returns information about the host attached to this service.
     *
     * <p><b>This method should not be called until the {@link Lifecycle.State} of the context's
     * {@link Session} is at least {@link Lifecycle.State#CREATED}</b>.
     *
     * @return The {@link HostInfo} of the connected host, or {@code null} if it is not available.
     * @see HostInfo
     */
    @Nullable
    public HostInfo getHostInfo() {
        return mHostInfo;
    }
    /**
     * Requests the provided {@code permissions} from the user, calling the provided {@code
     * listener} in the main thread.
     *
     * <p>The app can define a branded background to the permission request through the
     * <code>carPermissionActivityLayout</code> theme attribute, by declaring it in a theme and
     * referencing the theme from the <code>androidx.car.app.theme</code> metadata.
     *
     * <p>In <code>AndroidManifest.xml</code>, under the <code>application</code> element
     * corresponding to the car app:
     *
     * <pre>{@code
     * <meta-data
     *   android:name="androidx.car.app.theme"
     *   android:resource="@style/CarAppTheme"/>
     * }</pre>
     *
     * The <code>CarAppTheme</code> style is defined as any other themes in a resource file:
     *
     * <pre>{@code
     * <resources>
     *   <style name="CarAppTheme">
     *     <item name="carPermissionActivityLayout">@layout/app_branded_background</item>
     *   </style>
     * </resources>
     * }</pre>
     *
     * <p>The default behavior is to have no background behind the permission request.
     *
     * @see CarContext#requestPermissions(List, Executor, OnRequestPermissionsListener)=
     */
    public void requestPermissions(@NonNull List<String> permissions,
            @NonNull OnRequestPermissionsListener listener) {
        requestPermissions(permissions, ContextCompat.getMainExecutor(this), listener);
    }
    /**
     * Requests the provided {@code permissions} from the user.
     *
     * <p>When the result is available, the {@code listener} provided will be called using the
     * {@link Executor} provided.
     *
     * <p>This method should be called using a
     * {@link androidx.car.app.model.ParkedOnlyOnClickListener}.
     *
     * <p>If this method is called while the host deems it is unsafe (for example, when the user
     * is driving), the permission(s) will not be requested from the user.
     *
     * <p>If the {@link Session} is destroyed before the user accepts or rejects the permissions,
     * the callback will not be executed.
     *
     * <h4>Platform Considerations</h4>
     *
     * Using this method allows the app to work across all platforms supported by the library with
     * the same API (e.g. Android Auto on mobile devices and Android Automotive OS on native car
     * heads unit). On a mobile platform, this method will start an activity that will display the
     * platform's permissions UI over it. You can choose to not
     * use this method and instead implement your own activity and code to request the
     * permissions in that platform. On Automotive OS however, distraction-optimized activities
     * other than {@code CarAppActivity} are not allowed and may be
     * rejected during app submission. See {@code CarAppActivity} for
     * more details.
     *
     * @param permissions the runtime permissions to request from the user
     * @param executor    the executor that will be used for calling the {@code callback} provided
     * @param listener    listener that will be notified when the user takes action on the
     *                    permission request
     * @throws NullPointerException if any of {@code executor}, {@code permissions} or
     *                              {@code callback} are {@code null}
     */
    public void requestPermissions(@NonNull List<String> permissions,
            @NonNull /* @CallbackExecutor */ Executor executor,
            @NonNull OnRequestPermissionsListener listener) {
        requireNonNull(executor);
        requireNonNull(permissions);
        requireNonNull(listener);
        ComponentName appActivityComponent = new ComponentName(this,
                CarAppPermissionActivity.class);
        Lifecycle lifecycle = mLifecycle;
        Bundle extras = new Bundle(2);
        extras.putBinder(EXTRA_ON_REQUEST_PERMISSIONS_RESULT_LISTENER_KEY,
                new IOnRequestPermissionsListener.Stub() {
                    @Override
                    public void onRequestPermissionsResult(String[] approvedPermissions,
                            String[] rejectedPermissions) {
                        if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.CREATED)) {
                            List<String> approved = Arrays.asList(approvedPermissions);
                            List<String> rejected = Arrays.asList(rejectedPermissions);
                            executor.execute(() -> listener.onRequestPermissionsResult(approved,
                                    rejected));
                        }
                    }
                }.asBinder());
        extras.putStringArray(EXTRA_PERMISSIONS_KEY, permissions.toArray(new String[0]));
        Intent intent =
                new Intent(REQUEST_PERMISSIONS_ACTION).setComponent(appActivityComponent)
                        .putExtras(extras)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Bundle activityOptionsBundle = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            activityOptionsBundle = Api26Impl.makeBasicActivityOptionsBundle();
        }
        startActivity(intent, activityOptionsBundle);
    }
    @RestrictTo(LIBRARY_GROUP) // Restrict to testing library
    @MainThread
    public void setCarHost(@NonNull ICarHost carHost) {
        ThreadUtils.checkMainThread();
        mHostDispatcher.setCarHost(requireNonNull(carHost));
    }
    /**
     * Copies the fields from the provided {@link Configuration} into the {@link Configuration}
     * contained in this object.
     *
     */
    @RestrictTo(LIBRARY)
    @MainThread
    @SuppressWarnings("deprecation")
    void onCarConfigurationChanged(@NonNull Configuration configuration) {
        ThreadUtils.checkMainThread();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG,
                    "Car configuration changed, configuration: " + configuration
                            + ", displayMetrics: "
                            + getResources().getDisplayMetrics());
        }
        getResources()
                .updateConfiguration(requireNonNull(configuration),
                        getResources().getDisplayMetrics());
    }
    /**
     * Updates context information based on the information provided during connection handshake
     */
    @RestrictTo(LIBRARY_GROUP)
    @MainThread
    public void updateHandshakeInfo(@NonNull HandshakeInfo handshakeInfo) {
        mCarAppApiLevel = handshakeInfo.getHostCarAppApiLevel();
    }
    /**
     * Updates host information based on the information provided during connection handshake
     *
     */
    @RestrictTo(LIBRARY)
    @MainThread
    void updateHostInfo(@NonNull HostInfo hostInfo) {
        mHostInfo = hostInfo;
    }
    /**
     * Attaches the base {@link Context} for this {@link CarContext} by creating a new display
     * context using {@link #createDisplayContext} with a {@link VirtualDisplay} created using
     * the metrics from the provided {@link Configuration}, and then also calling {@link
     * #createConfigurationContext} with the provided {@link Configuration}.
     *
     * <p>This call creates a display context and then a configuration context to ensure that
     * updates to the phone configuration do not update either the {@link Configuration} or {@link
     * android.util.DisplayMetrics} held by this {@link CarContext}'s resources.
     *
     */
    @RestrictTo(LIBRARY)
    @MainThread
    void attachBaseContext(@NonNull Context context, @NonNull Configuration configuration) {
        ThreadUtils.checkMainThread();
        // If this is the first time attaching the base, actually attach it, otherwise, just
        // update the configuration.
        if (getBaseContext() == null) {
            // Create the virtual display with the proper dimensions.
            VirtualDisplay display =
                    ((DisplayManager) requireNonNull(
                            context.getSystemService(Context.DISPLAY_SERVICE)))
                            .createVirtualDisplay(
                                    "CarAppService",
                                    configuration.screenWidthDp,
                                    configuration.screenHeightDp,
                                    configuration.densityDpi,
                                    null,
                                    VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
            attachBaseContext(
                    context
                            .createDisplayContext(display.getDisplay())
                            .createConfigurationContext(configuration));
        }
        onCarConfigurationChanged(configuration);
    }
    @RestrictTo(LIBRARY_GROUP)
    // Restrict to testing library
    ManagerCache getManagers() {
        return mManagers;
    }
    @RestrictTo(LIBRARY_GROUP) // Restrict to testing library
    @SuppressWarnings({
            "argument.type.incompatible",
            "method.invocation.invalid",
            "UnsafeOptInUsageError"
    }) // @UnderInitialization not available with androidx
    protected CarContext(@NonNull Lifecycle lifecycle, @NonNull HostDispatcher hostDispatcher) {
        super(null);
        mHostDispatcher = hostDispatcher;
        mManagers.addFactory(AppManager.class, APP_SERVICE,
                () -> AppManager.create(this, hostDispatcher, lifecycle));
        mManagers.addFactory(NavigationManager.class, NAVIGATION_SERVICE,
                () -> NavigationManager.create(this, hostDispatcher, lifecycle));
        mManagers.addFactory(ScreenManager.class, SCREEN_SERVICE,
                () -> ScreenManager.create(this, lifecycle));
        mManagers.addFactory(ConstraintManager.class, CONSTRAINT_SERVICE,
                () -> ConstraintManager.create(this, hostDispatcher));
        mManagers.addFactory(CarHardwareManager.class, HARDWARE_SERVICE,
                () -> CarHardwareManager.create(this, hostDispatcher));
        mManagers.addFactory(ResultManager.class, null,
                () -> ResultManager.create(this));
        mManagers.addFactory(SuggestionManager.class, SUGGESTION_SERVICE,
                () -> SuggestionManager.create(this, hostDispatcher, lifecycle));
        mManagers.addFactory(MediaPlaybackManager.class, MEDIA_PLAYBACK_SERVICE,
                () -> MediaPlaybackManager.create(this, hostDispatcher, lifecycle));
        mOnBackPressedDispatcher =
                new OnBackPressedDispatcher(() -> getCarService(ScreenManager.class).pop());
        mLifecycle = lifecycle;
        LifecycleObserver observer = new DefaultLifecycleObserver() {
            @Override
            public void onDestroy(@NonNull LifecycleOwner owner) {
                hostDispatcher.resetHosts();
                owner.getLifecycle().removeObserver(this);
            }
        };
        lifecycle.addObserver(observer);
    }
    @RequiresApi(api = VERSION_CODES.O)
    private static class Api26Impl {
        @DoNotInline
        static Bundle makeBasicActivityOptionsBundle() {
            return ActivityOptions.makeBasic()
                    .setLaunchDisplayId(Display.DEFAULT_DISPLAY).toBundle();
        }
    }
}