1 /*
2  * Copyright (C) 2021 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 androidx.window.extensions;
18 
19 import static android.view.WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15;
20 import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15;
21 
22 import android.app.ActivityThread;
23 import android.app.Application;
24 import android.app.compat.CompatChanges;
25 import android.content.Context;
26 import android.hardware.devicestate.DeviceStateManager;
27 import android.os.SystemProperties;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.VisibleForTesting;
33 import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
34 import androidx.window.common.RawFoldingFeatureProducer;
35 import androidx.window.extensions.area.WindowAreaComponent;
36 import androidx.window.extensions.area.WindowAreaComponentImpl;
37 import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
38 import androidx.window.extensions.embedding.SplitController;
39 import androidx.window.extensions.layout.WindowLayoutComponent;
40 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
41 
42 import java.util.Objects;
43 
44 
45 /**
46  * The reference implementation of {@link WindowExtensions} that implements the latest WindowManager
47  * Extensions APIs.
48  */
49 class WindowExtensionsImpl implements WindowExtensions {
50 
51     private static final String TAG = "WindowExtensionsImpl";
52 
53     /**
54      * The value of the system property that indicates no override is set.
55      */
56     private static final int NO_LEVEL_OVERRIDE = -1;
57 
58     /**
59      * The min version of the WM Extensions that must be supported in the current platform version.
60      */
61     @VisibleForTesting
62     static final int EXTENSIONS_VERSION_CURRENT_PLATFORM = 6;
63 
64     private final Object mLock = new Object();
65     private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
66     private volatile WindowLayoutComponentImpl mWindowLayoutComponent;
67     private volatile SplitController mSplitController;
68     private volatile WindowAreaComponent mWindowAreaComponent;
69 
70     private final int mVersion = EXTENSIONS_VERSION_CURRENT_PLATFORM;
71     private final boolean mIsActivityEmbeddingEnabled;
72 
WindowExtensionsImpl()73     WindowExtensionsImpl() {
74         mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled();
75 
76         Log.i(TAG, generateLogMessage());
77     }
78 
generateLogMessage()79     private String generateLogMessage() {
80         final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
81                 + "vendor API level=" + mVersion);
82         final int levelOverride = getLevelOverride();
83         if (levelOverride != NO_LEVEL_OVERRIDE) {
84             logBuilder.append(", override to ").append(levelOverride);
85         }
86         logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled);
87         return logBuilder.toString();
88     }
89 
90     // TODO(b/241126279) Introduce constants to better version functionality
91     @Override
getVendorApiLevel()92     public int getVendorApiLevel() {
93         final int levelOverride = getLevelOverride();
94         return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
95     }
96 
getLevelOverride()97     private int getLevelOverride() {
98         return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE);
99     }
100 
101     @NonNull
getApplication()102     private Application getApplication() {
103         return Objects.requireNonNull(ActivityThread.currentApplication());
104     }
105 
106     @NonNull
getDeviceStateManager()107     private DeviceStateManager getDeviceStateManager() {
108         return Objects.requireNonNull(getApplication().getSystemService(DeviceStateManager.class));
109     }
110 
111     @NonNull
getFoldingFeatureProducer()112     private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() {
113         if (mFoldingFeatureProducer == null) {
114             synchronized (mLock) {
115                 if (mFoldingFeatureProducer == null) {
116                     final Context context = getApplication();
117                     final RawFoldingFeatureProducer foldingFeatureProducer =
118                             new RawFoldingFeatureProducer(context);
119                     mFoldingFeatureProducer =
120                             new DeviceStateManagerFoldingFeatureProducer(context,
121                                     foldingFeatureProducer, getDeviceStateManager());
122                 }
123             }
124         }
125         return mFoldingFeatureProducer;
126     }
127 
128     @NonNull
getWindowLayoutComponentImpl()129     private WindowLayoutComponentImpl getWindowLayoutComponentImpl() {
130         if (mWindowLayoutComponent == null) {
131             synchronized (mLock) {
132                 if (mWindowLayoutComponent == null) {
133                     final Context context = getApplication();
134                     final DeviceStateManagerFoldingFeatureProducer producer =
135                             getFoldingFeatureProducer();
136                     mWindowLayoutComponent = new WindowLayoutComponentImpl(context, producer);
137                 }
138             }
139         }
140         return mWindowLayoutComponent;
141     }
142 
143     /**
144      * Returns a reference implementation of the latest {@link WindowLayoutComponent}.
145      *
146      * The implementation must match the API level reported in
147      * {@link WindowExtensions#getVendorApiLevel()}.
148      *
149      * @return {@link WindowLayoutComponent} OEM implementation
150      */
151     @NonNull
152     @Override
getWindowLayoutComponent()153     public WindowLayoutComponent getWindowLayoutComponent() {
154         return getWindowLayoutComponentImpl();
155     }
156 
157     /**
158      * Returns a reference implementation of the latest {@link ActivityEmbeddingComponent} if the
159      * device supports this feature, {@code null} otherwise.
160      *
161      * The implementation must match the API level reported in
162      * {@link WindowExtensions#getVendorApiLevel()}.
163      *
164      * @return {@link ActivityEmbeddingComponent} OEM implementation.
165      */
166     @Nullable
167     @Override
getActivityEmbeddingComponent()168     public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
169         if (!mIsActivityEmbeddingEnabled) {
170             return null;
171         }
172         if (mSplitController == null) {
173             synchronized (mLock) {
174                 if (mSplitController == null) {
175                     mSplitController = new SplitController(
176                             getWindowLayoutComponentImpl(),
177                             getFoldingFeatureProducer()
178                     );
179                 }
180             }
181         }
182         return mSplitController;
183     }
184 
185     /**
186      * Returns a reference implementation of the latest {@link WindowAreaComponent}
187      *
188      * The implementation must match the API level reported in
189      * {@link WindowExtensions#getVendorApiLevel()}.
190      *
191      * @return {@link WindowAreaComponent} OEM implementation.
192      */
193     @Nullable
194     @Override
getWindowAreaComponent()195     public WindowAreaComponent getWindowAreaComponent() {
196         if (mWindowAreaComponent == null) {
197             synchronized (mLock) {
198                 if (mWindowAreaComponent == null) {
199                     final Context context = getApplication();
200                     mWindowAreaComponent = new WindowAreaComponentImpl(context);
201                 }
202             }
203         }
204         return mWindowAreaComponent;
205     }
206 
207     @VisibleForTesting
isActivityEmbeddingEnabled()208     static boolean isActivityEmbeddingEnabled() {
209         if (!ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15) {
210             // Device enables it for all apps without targetSDK check.
211             // This must be true for all large screen devices.
212             return true;
213         }
214         // Use compat framework to guard the feature with targetSDK 15.
215         return CompatChanges.isChangeEnabled(ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15);
216     }
217 }
218