1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.car.projection;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.app.ActivityOptions;
24 import android.car.builtin.app.ActivityManagerHelper;
25 import android.content.ComponentName;
26 import android.os.Bundle;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 
31 /**
32  * This class holds OEM customization for projection receiver app.  It is created by Car Service.
33  *
34  * @hide
35  */
36 @SystemApi
37 public class ProjectionOptions {
38     private static final String KEY_PREFIX = "android.car.projection.";
39 
40     /** Immersive full screen mode (all system bars are hidden) */
41     public static final int UI_MODE_FULL_SCREEN = 0;
42 
43     /** Show status and navigation bars. */
44     public static final int UI_MODE_BLENDED = 1;
45 
46     private static final int UI_MODE_DEFAULT = UI_MODE_FULL_SCREEN;
47 
48     /** @hide */
49     @IntDef({UI_MODE_FULL_SCREEN, UI_MODE_BLENDED})
50     @Retention(RetentionPolicy.SOURCE)
51     public @interface ProjectionUiMode {}
52 
53     /** Indicates that head unit didn't specify information about access point mode.  This value
54      * can only be seen on Android SDK 31 and below. */
55     public static final int AP_MODE_NOT_SPECIFIED = 0;
56 
57     /** Projection access point was created such that it may provide Internet access. */
58     public static final int AP_MODE_TETHERED = 1;
59 
60     /**
61      * Projection access point was created as local-only hotspot, without Internet access and the
62      * credentials will be auto-generated for every access point initialization.
63      */
64     public static final int AP_MODE_LOHS_DYNAMIC_CREDENTIALS = 2;
65 
66     /**
67      * Projection access point was created as local-only hotspot, without Internet access and the
68      * credentials will persist reboots.  Credentials still can be reseted by user or app request.
69      */
70     public static final int AP_MODE_LOHS_STATIC_CREDENTIALS = 3;
71 
72     /** @hide */
73     @IntDef({AP_MODE_NOT_SPECIFIED, AP_MODE_TETHERED, AP_MODE_LOHS_DYNAMIC_CREDENTIALS,
74             AP_MODE_LOHS_STATIC_CREDENTIALS})
75     @Retention(RetentionPolicy.SOURCE)
76     public @interface ProjectionAccessPointMode {}
77 
78     private static final String KEY_ACTIVITY_OPTIONS = KEY_PREFIX + "activityOptions";
79     private static final String KEY_UI_MODE = KEY_PREFIX + "systemUiFlags";
80     private static final String KEY_CONSENT_ACTIVITY = KEY_PREFIX + "consentActivity";
81     private static final String KEY_ACCESS_POINT_MODE = KEY_PREFIX + "ap_mode";
82 
83     private final ActivityOptions mActivityOptions;
84     private final int mUiMode;
85     private final ComponentName mConsentActivity;
86     private final int mApMode;
87 
88     /**
89      * Creates new instance for given {@code Bundle}
90      *
91      * @param bundle contains OEM specific information
92      */
ProjectionOptions(Bundle bundle)93     public ProjectionOptions(Bundle bundle) {
94         Bundle activityOptionsBundle = bundle.getBundle(KEY_ACTIVITY_OPTIONS);
95         mActivityOptions = activityOptionsBundle != null
96                 ? ActivityManagerHelper.createActivityOptions(activityOptionsBundle) : null;
97         mUiMode = bundle.getInt(KEY_UI_MODE, UI_MODE_DEFAULT);
98         mConsentActivity = bundle.getParcelable(KEY_CONSENT_ACTIVITY);
99         mApMode = bundle.getInt(KEY_ACCESS_POINT_MODE, AP_MODE_NOT_SPECIFIED);
100     }
101 
ProjectionOptions(Builder builder)102     private ProjectionOptions(Builder builder) {
103         mActivityOptions = builder.mActivityOptions;
104         mUiMode = builder.mUiMode;
105         mConsentActivity = builder.mConsentActivity;
106         mApMode = builder.mApMode;
107     }
108 
109     /**
110      * Returns combination of flags from View.SYSTEM_UI_FLAG_* which will be used by projection
111      * receiver app during rendering.
112      */
getUiMode()113     public @ProjectionUiMode int getUiMode() {
114         return mUiMode;
115     }
116 
117     /**
118      * Returns projection access point mode.
119      *
120      * <p>The result could be one of the following values:
121      * <ul>
122      *     <li>{@link #AP_MODE_NOT_SPECIFIED}</li>
123      *     <li>{@link #AP_MODE_TETHERED}</li>
124      *     <li>{@link #AP_MODE_LOHS_DYNAMIC_CREDENTIALS}</li>
125      *     <li>{@link #AP_MODE_LOHS_STATIC_CREDENTIALS}</li>
126      * </ul>
127      */
getProjectionAccessPointMode()128     public @ProjectionAccessPointMode int getProjectionAccessPointMode() {
129         return mApMode;
130     }
131 
132     /**
133      * Returns {@link ActivityOptions} that needs to be applied when launching projection activity
134      */
getActivityOptions()135     public @Nullable ActivityOptions getActivityOptions() {
136         return mActivityOptions;
137     }
138 
139     /**
140      * Returns package/activity name of the consent activity provided by OEM which needs to be shown
141      * for all mobile devices unless user accepted the consent.
142      *
143      * <p>If the method returns null then consent dialog should not be shown.
144      */
getConsentActivity()145     public @Nullable ComponentName getConsentActivity() {
146         return mConsentActivity;
147     }
148 
149     /** Converts current object to {@link Bundle} */
toBundle()150     public @NonNull Bundle toBundle() {
151         Bundle bundle = new Bundle();
152         if (mActivityOptions != null) {
153             bundle.putBundle(KEY_ACTIVITY_OPTIONS, mActivityOptions.toBundle());
154         }
155         bundle.putParcelable(KEY_CONSENT_ACTIVITY, mConsentActivity);
156         if (mUiMode != UI_MODE_DEFAULT) {
157             bundle.putInt(KEY_UI_MODE, mUiMode);
158         }
159         bundle.putInt(KEY_ACCESS_POINT_MODE, mApMode);
160         return bundle;
161     }
162 
163     /** @hide */
builder()164     public static Builder builder() {
165         return new Builder();
166     }
167 
168     /** @hide */
169     public static class Builder {
170         private ActivityOptions mActivityOptions;
171         private int mUiMode = UI_MODE_DEFAULT;
172         private ComponentName mConsentActivity;
173         private int mApMode = AP_MODE_NOT_SPECIFIED;
174 
175         /** Sets {@link ActivityOptions} to launch projection activity. */
setProjectionActivityOptions(ActivityOptions activityOptions)176         public Builder setProjectionActivityOptions(ActivityOptions activityOptions) {
177             mActivityOptions = activityOptions;
178             return this;
179         }
180 
181         /** Set UI for projection activity. It can be one of {@code UI_MODE_*} constants. */
setUiMode(@rojectionUiMode int uiMode)182         public Builder setUiMode(@ProjectionUiMode int uiMode) {
183             mUiMode = uiMode;
184             return this;
185         }
186 
187         /** Sets consent activity which will be shown before starting projection. */
setConsentActivity(ComponentName consentActivity)188         public Builder setConsentActivity(ComponentName consentActivity) {
189             mConsentActivity = consentActivity;
190             return this;
191         }
192 
193         /** Sets projection access point mode */
setAccessPointMode(@rojectionAccessPointMode int accessPointMode)194         public Builder setAccessPointMode(@ProjectionAccessPointMode int accessPointMode) {
195             this.mApMode = accessPointMode;
196             return this;
197         }
198 
199         /** Creates an instance of {@link android.car.projection.ProjectionOptions} */
build()200         public ProjectionOptions build() {
201             return new ProjectionOptions(this);
202         }
203     }
204 
205     /** @hide */
206     @Override
toString()207     public String toString() {
208         return toBundle().toString();
209     }
210 }
211