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