Merge "SystemServiceManager: clarify onBootPhase timing log" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 5e068ee..9152843 100644
--- a/Android.bp
+++ b/Android.bp
@@ -967,6 +967,10 @@
"core/java/android/content/pm/DataLoaderParamsParcel.aidl",
"core/java/android/content/pm/DataLoaderType.aidl",
"core/java/android/content/pm/FileSystemControlParcel.aidl",
+ "core/java/android/content/pm/IDataLoader.aidl",
+ "core/java/android/content/pm/IDataLoaderManager.aidl",
+ "core/java/android/content/pm/InstallationFileParcel.aidl",
+ "core/java/android/content/pm/InstallationFileLocation.aidl",
"core/java/android/content/pm/IDataLoaderStatusListener.aidl",
"core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
"core/java/android/content/pm/NamedParcelFileDescriptor.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 85cff835..379ae57 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2018,16 +2018,13 @@
method @NonNull public final int getType();
}
- public final class InstallationFile implements android.os.Parcelable {
+ public final class InstallationFile {
ctor public InstallationFile(int, @NonNull String, long, @Nullable byte[], @Nullable byte[]);
- method public int describeContents();
method public long getLengthBytes();
method public int getLocation();
method @Nullable public byte[] getMetadata();
method @NonNull public String getName();
method @Nullable public byte[] getSignature();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallationFile> CREATOR;
}
public final class InstantAppInfo implements android.os.Parcelable {
@@ -10338,7 +10335,7 @@
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
- method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull String, @NonNull String) throws java.io.IOException;
+ method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
field public static final int FLAG_SESSION_TYPE_FUSE = 1; // 0x1
field public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java
index 2688038..4a61938 100644
--- a/core/java/android/content/pm/DataLoaderManager.java
+++ b/core/java/android/content/pm/DataLoaderManager.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Bundle;
import android.os.RemoteException;
/**
@@ -40,22 +39,19 @@
* Finds a data loader binder service and binds to it. This requires PackageManager.
*
* @param dataLoaderId ID for the new data loader binder service.
- * @param params Bundle that contains parameters to configure the data loader service.
- * Must contain:
- * key: "packageName", value: String, package name of data loader service
- * package;
- * key: "extras", value: Bundle, client-specific data structures
- *
+ * @param params DataLoaderParamsParcel object that contains data loader params, including
+ * its package name, class name, and additional parameters.
+ * @param control FileSystemControlParcel that contains filesystem control handlers.
* @param listener Callback for the data loader service to report status back to the
* caller.
* @return false if 1) target ID collides with a data loader that is already bound to data
* loader manager; 2) package name is not specified; 3) fails to find data loader package;
* or 4) fails to bind to the specified data loader service, otherwise return true.
*/
- public boolean initializeDataLoader(int dataLoaderId, @NonNull Bundle params,
- @NonNull IDataLoaderStatusListener listener) {
+ public boolean initializeDataLoader(int dataLoaderId, @NonNull DataLoaderParamsParcel params,
+ @NonNull FileSystemControlParcel control, @NonNull IDataLoaderStatusListener listener) {
try {
- return mService.initializeDataLoader(dataLoaderId, params, listener);
+ return mService.initializeDataLoader(dataLoaderId, params, control, listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl
index b5baa93..6a2658d 100644
--- a/core/java/android/content/pm/IDataLoader.aidl
+++ b/core/java/android/content/pm/IDataLoader.aidl
@@ -16,20 +16,22 @@
package android.content.pm;
-import android.os.Bundle;
+import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoaderStatusListener;
-import android.content.pm.InstallationFile;
+import android.content.pm.InstallationFileParcel;
import java.util.List;
/**
- * TODO: update with new APIs
* @hide
*/
oneway interface IDataLoader {
- void create(int id, in Bundle params, IDataLoaderStatusListener listener);
+ void create(int id, in DataLoaderParamsParcel params,
+ in FileSystemControlParcel control,
+ IDataLoaderStatusListener listener);
void start();
void stop();
void destroy();
- void prepareImage(in List<InstallationFile> addedFiles, in List<String> removedFiles);
+ void prepareImage(in InstallationFileParcel[] addedFiles, in @utf8InCpp String[] removedFiles);
}
diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl
index f453c9b..1336f72 100644
--- a/core/java/android/content/pm/IDataLoaderManager.aidl
+++ b/core/java/android/content/pm/IDataLoaderManager.aidl
@@ -16,14 +16,15 @@
package android.content.pm;
-import android.os.Bundle;
+import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
-import java.util.List;
/** @hide */
interface IDataLoaderManager {
- boolean initializeDataLoader(int id, in Bundle params, IDataLoaderStatusListener listener);
+ boolean initializeDataLoader(int id, in DataLoaderParamsParcel params,
+ in FileSystemControlParcel control, IDataLoaderStatusListener listener);
IDataLoader getDataLoader(int dataLoaderId);
void destroyDataLoader(int dataLoaderId);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index b449945..edc04c9 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -19,81 +19,47 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
/**
* Defines the properties of a file in an installation session.
* @hide
*/
@SystemApi
-public final class InstallationFile implements Parcelable {
- private final @PackageInstaller.FileLocation int mLocation;
- private final @NonNull String mName;
- private final long mLengthBytes;
- private final @Nullable byte[] mMetadata;
- private final @Nullable byte[] mSignature;
+public final class InstallationFile {
+ private final @NonNull InstallationFileParcel mParcel;
public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
- mLocation = location;
- mName = name;
- mLengthBytes = lengthBytes;
- mMetadata = metadata;
- mSignature = signature;
+ mParcel = new InstallationFileParcel();
+ mParcel.location = location;
+ mParcel.name = name;
+ mParcel.size = lengthBytes;
+ mParcel.metadata = metadata;
+ mParcel.signature = signature;
}
public @PackageInstaller.FileLocation int getLocation() {
- return mLocation;
+ return mParcel.location;
}
public @NonNull String getName() {
- return mName;
+ return mParcel.name;
}
public long getLengthBytes() {
- return mLengthBytes;
+ return mParcel.size;
}
public @Nullable byte[] getMetadata() {
- return mMetadata;
+ return mParcel.metadata;
}
public @Nullable byte[] getSignature() {
- return mSignature;
+ return mParcel.signature;
}
- private InstallationFile(Parcel source) {
- mLocation = source.readInt();
- mName = source.readString();
- mLengthBytes = source.readLong();
- mMetadata = source.createByteArray();
- mSignature = source.createByteArray();
+ /** @hide */
+ public @NonNull InstallationFileParcel getData() {
+ return mParcel;
}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mLocation);
- dest.writeString(mName);
- dest.writeLong(mLengthBytes);
- dest.writeByteArray(mMetadata);
- dest.writeByteArray(mSignature);
- }
-
- public static final @NonNull Creator<InstallationFile> CREATOR =
- new Creator<InstallationFile>() {
- public InstallationFile createFromParcel(Parcel source) {
- return new InstallationFile(source);
- }
-
- public InstallationFile[] newArray(int size) {
- return new InstallationFile[size];
- }
- };
-
}
diff --git a/core/java/android/content/pm/InstallationFileLocation.aidl b/core/java/android/content/pm/InstallationFileLocation.aidl
new file mode 100644
index 0000000..501640a
--- /dev/null
+++ b/core/java/android/content/pm/InstallationFileLocation.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.content.pm;
+
+/** @hide */
+@Backing(type="int")
+enum InstallationFileLocation {
+ UNKNOWN = -1,
+ DATA_APP = 0,
+ MEDIA_OBB = 1,
+ MEDIA_DATA = 2
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/InstallationFileParcel.aidl b/core/java/android/content/pm/InstallationFileParcel.aidl
new file mode 100644
index 0000000..b7efc19
--- /dev/null
+++ b/core/java/android/content/pm/InstallationFileParcel.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.content.pm;
+
+import android.content.pm.InstallationFileLocation;
+
+/**
+ * Describes a file which is part of a package installation.
+ * @hide
+ */
+parcelable InstallationFileParcel {
+ String name;
+ InstallationFileLocation location;
+ long size;
+ byte[] metadata;
+ byte[] signature;
+}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2acbb97..1f53176 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -372,7 +372,7 @@
* {@hide}
*/
@SystemApi
- public static final int LOCATION_DATA_APP = 0;
+ public static final int LOCATION_DATA_APP = InstallationFileLocation.DATA_APP;
/**
* Target location for the file in installation session is
@@ -380,7 +380,7 @@
* {@hide}
*/
@SystemApi
- public static final int LOCATION_MEDIA_OBB = 1;
+ public static final int LOCATION_MEDIA_OBB = InstallationFileLocation.MEDIA_OBB;
/**
* Target location for the file in installation session is
@@ -390,7 +390,7 @@
* {@hide}
*/
@SystemApi
- public static final int LOCATION_MEDIA_DATA = 2;
+ public static final int LOCATION_MEDIA_DATA = InstallationFileLocation.MEDIA_DATA;
/** @hide */
@IntDef(prefix = { "LOCATION_" }, value = {
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index ab224a2..251995a 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,7 +36,7 @@
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
-import android.content.pm.InstallationFile;
+import android.content.pm.InstallationFileParcel;
import android.text.TextUtils;
import android.util.Slog;
@@ -76,7 +76,7 @@
@NonNull File stageDir,
@NonNull DataLoaderParams dataLoaderParams,
@Nullable IDataLoaderStatusListener dataLoaderStatusListener,
- List<InstallationFile> addedFiles) throws IOException {
+ List<InstallationFileParcel> addedFiles) throws IOException {
// TODO(b/136132412): sanity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
@@ -94,17 +94,17 @@
result.mDefaultStorage.bind(stageDir.getAbsolutePath());
}
- for (InstallationFile file : addedFiles) {
- if (file.getLocation() == LOCATION_DATA_APP) {
+ for (InstallationFileParcel file : addedFiles) {
+ if (file.location == LOCATION_DATA_APP) {
try {
result.addApkFile(file);
} catch (IOException e) {
// TODO(b/146080380): add incremental-specific error code
throw new IOException(
- "Failed to add file to IncFS: " + file.getName() + ", reason: ", e);
+ "Failed to add file to IncFS: " + file.name + ", reason: ", e);
}
} else {
- throw new IOException("Unknown file location: " + file.getLocation());
+ throw new IOException("Unknown file location: " + file.location);
}
}
@@ -154,12 +154,11 @@
}
}
- private void addApkFile(@NonNull InstallationFile apk) throws IOException {
- final String apkName = apk.getName();
+ private void addApkFile(@NonNull InstallationFileParcel apk) throws IOException {
+ final String apkName = apk.name;
final File targetFile = new File(mStageDir, apkName);
if (!targetFile.exists()) {
- mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
- apk.getSignature());
+ mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature);
}
}
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 4190001..5bf1975 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -27,8 +27,8 @@
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.InstallationFile;
+import android.content.pm.InstallationFileParcel;
import android.content.pm.NamedParcelFileDescriptor;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.util.ExceptionUtils;
@@ -36,7 +36,6 @@
import java.io.IOException;
import java.util.Collection;
-import java.util.List;
/**
* The base class for implementing data loader service to control data loaders. Expecting
@@ -105,18 +104,11 @@
private int mId;
@Override
- public void create(int id, @NonNull Bundle options,
+ public void create(int id, @NonNull DataLoaderParamsParcel params,
+ @NonNull FileSystemControlParcel control,
@NonNull IDataLoaderStatusListener listener)
- throws IllegalArgumentException, RuntimeException {
+ throws RuntimeException {
mId = id;
- final DataLoaderParamsParcel params = options.getParcelable("params");
- if (params == null) {
- throw new IllegalArgumentException("Must specify data loader params");
- }
- final FileSystemControlParcel control = options.getParcelable("control");
- if (control == null) {
- throw new IllegalArgumentException("Must specify control parcel");
- }
try {
if (!nativeCreateDataLoader(id, control, params, listener)) {
Slog.e(TAG, "Failed to create native loader for " + mId);
@@ -178,7 +170,7 @@
}
@Override
- public void prepareImage(List<InstallationFile> addedFiles, List<String> removedFiles) {
+ public void prepareImage(InstallationFileParcel[] addedFiles, String[] removedFiles) {
if (!nativePrepareImage(mId, addedFiles, removedFiles)) {
Slog.w(TAG, "Failed to destroy loader: " + mId);
}
@@ -240,7 +232,7 @@
private native boolean nativeDestroyDataLoader(int storageId);
private native boolean nativePrepareImage(int storageId,
- List<InstallationFile> addedFiles, List<String> removedFiles);
+ InstallationFileParcel[] addedFiles, String[] removedFiles);
private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
long lengthBytes, ParcelFileDescriptor incomingFd);
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index cc8116d0..fe797eb 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -31,6 +31,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
+import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -109,10 +110,17 @@
*
* <p> Additional calls to start a session for the same {@code sessionId} while the session
* is still starting or already started should have no effect.
+ *
+ * @param sessionId uniquely identifies a running session and used in {@link #onEndSession}
+ * @param flag specifies the type or additional attributes of a session
+ * @param deviceFd for intercepting IO from other apps
+ * @param upperFileSystemPath is the root path on which we are intercepting IO from other apps
+ * @param lowerFileSystemPath is the root path matching {@code upperFileSystemPath} containing
+ * the actual data apps are trying to access
*/
public abstract void onStartSession(@NonNull String sessionId, @SessionFlag int flag,
- @NonNull ParcelFileDescriptor deviceFd, @NonNull String upperFileSystemPath,
- @NonNull String lowerFileSystemPath) throws IOException;
+ @NonNull ParcelFileDescriptor deviceFd, @NonNull File upperFileSystemPath,
+ @NonNull File lowerFileSystemPath) throws IOException;
/**
* Called when the system ends the session identified by {@code sessionId}. Implementors should
@@ -136,7 +144,8 @@
RemoteCallback callback) throws RemoteException {
mHandler.post(() -> {
try {
- onStartSession(sessionId, flag, deviceFd, upperPath, lowerPath);
+ onStartSession(sessionId, flag, deviceFd, new File(upperPath),
+ new File(lowerPath));
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
index ed0d381..5d9cd56 100644
--- a/core/jni/android_service_DataLoaderService.cpp
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -50,8 +50,8 @@
return DataLoaderService_OnDestroy(env, storageId);
}
-
-static jboolean nativePrepareImage(JNIEnv* env, jobject thiz, jint storageId, jobject addedFiles, jobject removedFiles) {
+static jboolean nativePrepareImage(JNIEnv* env, jobject thiz, jint storageId,
+ jobjectArray addedFiles, jobjectArray removedFiles) {
return DataLoaderService_OnPrepareImage(env, storageId, addedFiles, removedFiles);
}
@@ -75,7 +75,9 @@
{"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
{"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
{"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
- {"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage},
+ {"nativePrepareImage",
+ "(I[Landroid/content/pm/InstallationFileParcel;[Ljava/lang/String;)Z",
+ (void*)nativePrepareImage},
{"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V",
(void*)nativeWriteData},
};
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 963f813..afa58d5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -392,6 +392,8 @@
<permission name="android.permission.MANAGE_NOTIFICATIONS"/>
<!-- Permission required for CompanionDeviceManager CTS test. -->
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
+ <!-- Permission required for testing registering pull atom callbacks. -->
+ <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index f3b922e..fe4fdd3 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -27,6 +27,7 @@
android:theme="@style/Theme.AlertDialogActivity"
android:supportsRtl="true"
android:defaultToDeviceProtectedStorage="true"
+ android:forceQueryable="true"
android:directBootAware="true">
<receiver android:name=".TemporaryFileManager"
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 241c512..e13e49f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -273,6 +273,9 @@
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
+ <!-- Permission needed to test registering pull atom callbacks -->
+ <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 3db01a4..63eccda5 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -23,6 +23,7 @@
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:accessibilityLiveRegion="polite"
android:background="@color/biometric_dialog_dim_color"
android:contentDescription="@string/biometric_dialog_empty_space_description"/>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 1ae3d4f..60c1a94 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -225,8 +225,8 @@
*/
Rect getDestinationBounds(float aspectRatio, Rect bounds) {
final Rect destinationBounds;
- final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
if (bounds == null) {
+ final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
destinationBounds = new Rect(defaultBounds);
} else {
destinationBounds = new Rect(bounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 665146e..1566877 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -18,7 +18,7 @@
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
-import static com.android.systemui.pip.PipAnimationController.DURATION_NONE;
+import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -87,19 +87,7 @@
}
});
final Rect destinationBounds = animator.getDestinationBounds();
- mLastReportedBounds.set(destinationBounds);
- try {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (animator.shouldScheduleFinishPip()) {
- wct.scheduleFinishEnterPip(wc, destinationBounds);
- } else {
- wct.setBounds(wc, destinationBounds);
- }
- wct.setBoundsChangeTransaction(wc, tx);
- mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to apply container transaction", e);
- }
+ finishResizeInternal(destinationBounds, wc, tx, animator.shouldScheduleFinishPip());
}
@Override
@@ -125,15 +113,6 @@
}
/**
- * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE}
- */
- public void resizePinnedStack(Rect destinationBounds, int durationMs) {
- Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
- resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */,
- mLastReportedBounds, destinationBounds, durationMs);
- }
-
- /**
* Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
*/
public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
@@ -143,7 +122,7 @@
}
final Rect destinationBounds = new Rect(originalBounds);
destinationBounds.offset(xOffset, yOffset);
- resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
+ animateResizePipInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
originalBounds, destinationBounds, durationMs);
}
@@ -208,15 +187,14 @@
mTaskInfo = info;
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
- resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */,
- currentBounds, destinationBounds,
- PipAnimationController.DURATION_DEFAULT_MS);
+ animateResizePipInternal(mTaskInfo.token, true /* scheduleFinishPip */,
+ currentBounds, destinationBounds, DURATION_DEFAULT_MS);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
mMainHandler.post(() -> mPipAnimationController
.getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
destinationBounds, 0f, 1f)
.setPipAnimationCallback(mPipAnimationCallback)
- .setDuration(PipAnimationController.DURATION_DEFAULT_MS)
+ .setDuration(DURATION_DEFAULT_MS)
.start());
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
@@ -231,9 +209,8 @@
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- resizePinnedStackInternal(token, false /* scheduleFinishPip */,
- mLastReportedBounds, mDisplayBounds,
- PipAnimationController.DURATION_DEFAULT_MS);
+ animateResizePipInternal(token, false /* scheduleFinishPip */,
+ mLastReportedBounds, mDisplayBounds, DURATION_DEFAULT_MS);
}
@Override
@@ -245,11 +222,22 @@
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
- resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS);
+ animateResizePip(destinationBounds, DURATION_DEFAULT_MS);
}
- private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip,
- Rect currentBounds, Rect destinationBounds, int animationDurationMs) {
+
+ /**
+ * Directly perform manipulation/resize on the leash. This will not perform any
+ * {@link WindowContainerTransaction} until {@link #finishResize} is called.
+ */
+ public void resizePip(Rect destinationBounds) {
+ Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
+ resizePipInternal(mTaskInfo.token, destinationBounds);
+ }
+
+ private void resizePipInternal(IWindowContainer wc,
+ Rect destinationBounds) {
+ Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
try {
// Could happen when dismissPip
if (wc == null || wc.getLeash() == null) {
@@ -257,20 +245,10 @@
return;
}
final SurfaceControl leash = wc.getLeash();
- if (animationDurationMs == DURATION_NONE) {
- // Directly resize if no animation duration is set. When fling, wait for final
- // callback to issue the proper WindowContainerTransaction with destination bounds.
- new SurfaceControl.Transaction()
- .setPosition(leash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
- .apply();
- } else {
- mMainHandler.post(() -> mPipAnimationController
- .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
- .setPipAnimationCallback(mPipAnimationCallback)
- .setDuration(animationDurationMs)
- .start());
- }
+ new SurfaceControl.Transaction()
+ .setPosition(leash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+ .apply();
} catch (RemoteException e) {
Log.w(TAG, "Abort animation, invalid window container", e);
} catch (Exception e) {
@@ -278,6 +256,73 @@
}
}
+ /**
+ * Finish a intermediate resize operation. This is expected to be called after
+ * {@link #resizePip}.
+ */
+ public void finishResize(Rect destinationBounds) {
+ try {
+ final IWindowContainer wc = mTaskInfo.token;
+ SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
+ .setPosition(wc.getLeash(), destinationBounds.left,
+ destinationBounds.top)
+ .setWindowCrop(wc.getLeash(), destinationBounds.width(),
+ destinationBounds.height());
+ finishResizeInternal(destinationBounds, wc, tx, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to obtain leash");
+ }
+ }
+
+ private void finishResizeInternal(Rect destinationBounds, IWindowContainer wc,
+ SurfaceControl.Transaction tx, boolean shouldScheduleFinishPip) {
+ mLastReportedBounds.set(destinationBounds);
+ try {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (shouldScheduleFinishPip) {
+ wct.scheduleFinishEnterPip(wc, destinationBounds);
+ } else {
+ wct.setBounds(wc, destinationBounds);
+ }
+ wct.setBoundsChangeTransaction(mTaskInfo.token, tx);
+ mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to apply container transaction", e);
+ }
+ }
+
+ /**
+ * Animates resizing of the pinned stack given the duration.
+ */
+ public void animateResizePip(Rect destinationBounds, int durationMs) {
+ Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
+ animateResizePipInternal(mTaskInfo.token, false, mLastReportedBounds,
+ destinationBounds, durationMs);
+ }
+
+ private void animateResizePipInternal(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect currentBounds, Rect destinationBounds, int durationMs) {
+ try {
+ // Could happen when dismissPip
+ if (wc == null || wc.getLeash() == null) {
+ Log.w(TAG, "Abort animation, invalid leash");
+ return;
+ }
+ final SurfaceControl leash = wc.getLeash();
+
+ mMainHandler.post(() -> mPipAnimationController
+ .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(durationMs)
+ .start());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Abort animation, invalid window container", e);
+ } catch (Exception e) {
+ Log.e(TAG, "Should not reach here, terrible thing happened", e);
+ }
+ }
+
+
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
return params == null
? mPipBoundsHandler.getDefaultAspectRatio()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 8ada3c3..6b3b748 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -168,7 +168,7 @@
// bounds. We want to restore to the unexpanded bounds when re-entering pip,
// so we save the bounds before expansion (normal) instead of the current
// bounds.
- mReentryBounds.set(mTouchHandler.getNormalBounds());
+ mReentryBounds.set(mTouchHandler.getMinBounds());
// Apply the snap fraction of the current bounds to the normal bounds.
float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
mPipBoundsHandler.applySnapFraction(mReentryBounds, snapFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 980d18b..dece850 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -38,7 +38,6 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.SomeArgs;
-import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -546,7 +545,7 @@
case MSG_RESIZE_IMMEDIATE: {
SomeArgs args = (SomeArgs) msg.obj;
Rect toBounds = (Rect) args.arg1;
- mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE);
+ mPipTaskOrganizer.resizePip(toBounds);
mBounds.set(toBounds);
return true;
}
@@ -564,7 +563,7 @@
return true;
}
- mPipTaskOrganizer.resizePinnedStack(toBounds, duration);
+ mPipTaskOrganizer.animateResizePip(toBounds, duration);
mBounds.set(toBounds);
} catch (RemoteException e) {
Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 389793e..5926b89 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -41,6 +41,7 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -64,6 +65,7 @@
private final PointF mDownPoint = new PointF();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
+ private final Rect mLastResizeBounds = new Rect();
private final Rect mTmpBounds = new Rect();
private final int mDelta;
@@ -74,12 +76,13 @@
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
+ private PipTaskOrganizer mPipTaskOrganizer;
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) {
final Resources res = context.getResources();
context.getDisplay().getMetrics(mDisplayMetrics);
mDisplayId = context.getDisplayId();
@@ -87,6 +90,7 @@
mPipBoundsHandler = pipBoundsHandler;
mPipTouchHandler = pipTouchHandler;
mMotionHelper = motionHelper;
+ mPipTaskOrganizer = pipTaskOrganizer;
context.getDisplay().getRealSize(mMaxSize);
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
@@ -190,11 +194,7 @@
}
} else if (mAllowGesture) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
- Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
- mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
- true, true);
- mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
+
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
// We do not support multi touch for resizing via drag
@@ -203,12 +203,18 @@
case MotionEvent.ACTION_MOVE:
// Capture inputs
mInputMonitor.pilferPointers();
- //TODO: Actually do resize here.
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true, true));
+ mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+ mPipTaskOrganizer.resizePip(mLastResizeBounds);
+
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- //TODO: Finish resize operation here.
- mMotionHelper.synchronizePinnedStackBounds();
+ mPipTaskOrganizer.finishResize(mLastResizeBounds);
+ mLastResizeBounds.setEmpty();
mCtrlType = CTRL_NONE;
mAllowGesture = false;
break;
@@ -220,7 +226,7 @@
mMaxSize.set(maxX, maxY);
}
- void updateMiniSize(int minX, int minY) {
+ void updateMinSize(int minX, int minY) {
mMinSize.set(minX, minY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3f73d01..bd22480 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -41,6 +41,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
@@ -73,7 +74,7 @@
private final Context mContext;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
- private final PipResizeGestureHandler mPipResizeGestureHandler;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -84,14 +85,22 @@
// The current movement bounds
private Rect mMovementBounds = new Rect();
+ // The current resized bounds, changed by user resize.
+ // Note that this is not necessarily the same as PipMotionHelper#getBounds, since it's possible
+ // that PIP is currently is in a expanded state (max size) but we still need mResizeBounds to
+ // know what size to restore to once expand animation times out.
+ @VisibleForTesting Rect mResizedBounds = new Rect();
// The reference inset bounds, used to determine the dismiss fraction
private Rect mInsetBounds = new Rect();
- // The reference bounds used to calculate the normal/expanded target bounds
- private Rect mNormalBounds = new Rect();
- private Rect mNormalMovementBounds = new Rect();
- private Rect mExpandedBounds = new Rect();
- private Rect mExpandedMovementBounds = new Rect();
+
+ // The reference bounds used to calculate the minimum/maximum target bounds
+ // The bound in which PIP enters is the starting/minimum bound, while the expanded/auto-resized
+ // bound is the maximum bound.
+ private Rect mMinBounds = new Rect();
+ @VisibleForTesting Rect mMinMovementBounds = new Rect();
+ private Rect mMaxBounds = new Rect();
+ @VisibleForTesting Rect mMaxMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -126,7 +135,7 @@
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
private final FloatingContentCoordinator mFloatingContentCoordinator;
- private final PipMotionHelper mMotionHelper;
+ private PipMotionHelper mMotionHelper;
private PipTouchGesture mGesture;
// Temp vars
@@ -182,7 +191,7 @@
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
- deviceConfig);
+ deviceConfig, pipTaskOrganizer);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -235,14 +244,16 @@
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
}
+ mResizedBounds.setEmpty();
mPipResizeGestureHandler.onActivityUnpinned();
}
public void onPinnedStackAnimationEnded() {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
- mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
- mMotionHelper.getBounds().height());
+
+ updateMovementBounds();
+ mResizedBounds.set(mMinBounds);
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -266,7 +277,10 @@
mShelfHeight = shelfHeight;
}
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
+ /**
+ * Update all the cached bounds (movement, min, max, etc.)
+ */
+ public void onMovementBoundsChanged(Rect insetBounds, Rect minBounds, Rect curBounds,
boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation);
@@ -275,23 +289,25 @@
}
// Re-calculate the expanded bounds
- mNormalBounds = normalBounds;
- Rect normalMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
+ mMinBounds.set(minBounds);
+ Rect minMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mMinBounds, insetBounds, minMovementBounds,
bottomOffset);
// Calculate the expanded size
- float aspectRatio = (float) normalBounds.width() / normalBounds.height();
+ float aspectRatio = (float) minBounds.width() / minBounds.height();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
+ Size maxSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
- mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
- mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
- Rect expandedMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
+ mMaxBounds.set(0, 0, maxSize.getWidth(), maxSize.getHeight());
+ Rect maxMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mMaxBounds, insetBounds, maxMovementBounds,
bottomOffset);
+ mPipResizeGestureHandler.updateMinSize(minBounds.width(), minBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mMaxBounds.width(), mMaxBounds.height());
+
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
int extraOffset = Math.max(
@@ -308,8 +324,8 @@
final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
* mContext.getResources().getDisplayMetrics().density;
final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
- ? new Rect(expandedMovementBounds)
- : new Rect(normalMovementBounds);
+ ? new Rect(maxMovementBounds)
+ : new Rect(minMovementBounds);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
final int toBottom = toMovementBounds.bottom < toMovementBounds.top
? toMovementBounds.bottom
@@ -323,17 +339,17 @@
// Update the movement bounds after doing the calculations based on the old movement bounds
// above
- mNormalMovementBounds = normalMovementBounds;
- mExpandedMovementBounds = expandedMovementBounds;
+ mMinMovementBounds = minMovementBounds;
+ mMaxMovementBounds = maxMovementBounds;
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
mMovementBoundsExtraOffsets = extraOffset;
// If we have a deferred resize, apply it now
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
- mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, true /* immediate */);
+ mMotionHelper.animateToUnexpandedState(minBounds, mSavedSnapFraction,
+ mMinMovementBounds, mMovementBounds, true /* immediate */);
mSavedSnapFraction = -1f;
mDeferResizeToNormalBoundsUntilRotation = -1;
}
@@ -387,7 +403,7 @@
case MotionEvent.ACTION_UP: {
// Update the movement bounds again if the state has changed since the user started
// dragging (ie. when the IME shows)
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
if (mGesture.onUp(mTouchState)) {
break;
@@ -485,11 +501,13 @@
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
- // fraction and snap to the closest edge
- Rect expandedBounds = new Rect(mExpandedBounds);
+ // fraction and snap to the closest edge.
+ // Also save the current resized bounds so when the menu disappears, we can restore it.
if (resize) {
+ mResizedBounds.set(mMotionHelper.getBounds());
+ Rect expandedBounds = new Rect(mMaxBounds);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
- mMovementBounds, mExpandedMovementBounds);
+ mMovementBounds, mMaxMovementBounds);
}
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
@@ -515,9 +533,9 @@
}
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
- Rect normalBounds = new Rect(mNormalBounds);
+ Rect normalBounds = new Rect(mResizedBounds);
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, false /* immediate */);
+ mMinMovementBounds, mMovementBounds, false /* immediate */);
mSavedSnapFraction = -1f;
}
} else {
@@ -528,7 +546,7 @@
}
}
mMenuState = menuState;
- updateMovementBounds(menuState);
+ updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
onRegistrationChanged(menuState == MENU_STATE_NONE);
@@ -544,11 +562,26 @@
return mMotionHelper;
}
+ @VisibleForTesting
+ PipResizeGestureHandler getPipResizeGestureHandler() {
+ return mPipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipResizeGestureHandler(PipResizeGestureHandler pipResizeGestureHandler) {
+ mPipResizeGestureHandler = pipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipMotionHelper(PipMotionHelper pipMotionHelper) {
+ mMotionHelper = pipMotionHelper;
+ }
+
/**
* @return the unexpanded bounds.
*/
- public Rect getNormalBounds() {
- return mNormalBounds;
+ public Rect getMinBounds() {
+ return mMinBounds;
}
/**
@@ -701,17 +734,17 @@
};
/**
- * Updates the current movement bounds based on whether the menu is currently visible and
- * resized.
+ * Updates the current movement bounds based on the current PIP size.
*/
- private void updateMovementBounds(int menuState) {
- boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded && willResizeMenu()
- ? mExpandedMovementBounds
- : mNormalMovementBounds;
+ private void updateMovementBounds() {
+ Rect movementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
+ movementBounds, mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.setCurrentMovementBounds(movementBounds);
+
+ boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
mPipBoundsHandler.setMinEdgeSize(
- isMenuExpanded ? mExpandedShortestEdgeSize : 0);
- mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+ isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
}
/**
@@ -729,18 +762,18 @@
if (!mEnableResize) {
return false;
}
- return mExpandedBounds.width() != mNormalBounds.width()
- || mExpandedBounds.height() != mNormalBounds.height();
+ return mMaxBounds.width() != mMinBounds.width()
+ || mMaxBounds.height() != mMinBounds.height();
}
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
- pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
- pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
- pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
- pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
+ pw.println(innerPrefix + "mMinBounds=" + mMinBounds);
+ pw.println(innerPrefix + "mMinMovementBounds=" + mMinMovementBounds);
+ pw.println(innerPrefix + "mMaxBounds=" + mMaxBounds);
+ pw.println(innerPrefix + "mMaxMovementBounds=" + mMaxMovementBounds);
pw.println(innerPrefix + "mMenuState=" + mMenuState);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index cb1a218..f28c3f6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -433,8 +433,8 @@
mCurrentPipBounds = mPipBounds;
break;
}
- mPipTaskOrganizer.resizePinnedStack(
- mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS);
+ mPipTaskOrganizer.animateResizePip(mCurrentPipBounds,
+ PipAnimationController.DURATION_DEFAULT_MS);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
new file mode 100644
index 0000000..35abb14
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipTouchHandler}, including but not limited to:
+ * - Update movement bounds based on new bounds
+ * - Update movement bounds based on IME/shelf
+ * - Update movement bounds to PipResizeHandler
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipTouchHandlerTest extends SysuiTestCase {
+ private static final int ROUNDING_ERROR_MARGIN = 10;
+ private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final Rect EMPTY_CURRENT_BOUNDS = null;
+
+ private PipTouchHandler mPipTouchHandler;
+ private DisplayInfo mDefaultDisplayInfo;
+
+ @Mock
+ private IActivityManager mActivityManager;
+
+ @Mock
+ private IActivityTaskManager mIActivityTaskManager;
+
+ @Mock
+ private PipMenuActivityController mPipMenuActivityController;
+
+ @Mock
+ private InputConsumerController mInputConsumerController;
+
+ @Mock
+ private PipBoundsHandler mPipBoundsHandler;
+
+ @Mock
+ private PipTaskOrganizer mPipTaskOrganizer;
+
+ @Mock
+ private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ @Mock
+ private DeviceConfigProxy mDeviceConfigProxy;
+
+ private PipSnapAlgorithm mPipSnapAlgorithm;
+ private PipMotionHelper mMotionHelper;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
+
+ Rect mInsetBounds;
+ Rect mMinBounds;
+ Rect mCurBounds;
+ boolean mFromImeAdjustment;
+ boolean mFromShelfAdjustment;
+ int mDisplayRotation;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
+ mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
+ mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy);
+ mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+ mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
+ mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
+ mPipTouchHandler.setPipMotionHelper(mMotionHelper);
+ mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
+
+ // Assume a display of 1000 x 1000
+ // inset of 10
+ mInsetBounds = new Rect(10, 10, 990, 990);
+ // minBounds of 100x100 bottom right corner
+ mMinBounds = new Rect(890, 890, 990, 990);
+ mCurBounds = new Rect();
+ mFromImeAdjustment = false;
+ mFromShelfAdjustment = false;
+ mDisplayRotation = 0;
+ }
+
+ @Test
+ public void updateMovementBounds_minBounds() {
+ Rect expectedMinMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMinMovementBounds, mPipTouchHandler.mMinMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMinSize(mMinBounds.width(), mMinBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_maxBounds() {
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
+ Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
+ Rect expectedMaxMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mMaxMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMaxSize(maxBounds.width(), maxBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_withImeAdjustment_movesPip() {
+ mFromImeAdjustment = true;
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
+ }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 3fd1b78..423dd52 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1438,7 +1438,6 @@
}
int capabilityFromFGS = 0; // capability from foreground service.
- boolean procStateFromFGSClient = false;
for (int is = app.services.size() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -1561,10 +1560,6 @@
continue;
}
- if (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE) {
- procStateFromFGSClient = true;
- }
-
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= client.curCapability;
}
@@ -2012,11 +2007,8 @@
} else if (!ActivityManager.isProcStateBackground(procState)) {
// procState higher than PROCESS_STATE_BOUND_FOREGROUND_SERVICE implicitly has
// camera/microphone capability
- if (procState == PROCESS_STATE_FOREGROUND_SERVICE && procStateFromFGSClient) {
- // if the FGS state is passed down from client, do not grant implicit capabilities.
- } else {
- capability |= PROCESS_CAPABILITY_ALL_IMPLICIT;
- }
+ //TODO: remove this line when enforcing the feature.
+ capability |= PROCESS_CAPABILITY_ALL_IMPLICIT;
}
// TOP process has all capabilities.
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 1320e6c..cadb9d0 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -17,14 +17,12 @@
package com.android.server.incremental;
import android.annotation.NonNull;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.DataLoaderManager;
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.incremental.IIncrementalManager;
@@ -93,17 +91,12 @@
public boolean prepareDataLoader(int mountId, FileSystemControlParcel control,
DataLoaderParamsParcel params,
IDataLoaderStatusListener listener) {
- Bundle dataLoaderParams = new Bundle();
- dataLoaderParams.putParcelable("componentName",
- new ComponentName(params.packageName, params.className));
- dataLoaderParams.putParcelable("control", control);
- dataLoaderParams.putParcelable("params", params);
DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
if (dataLoaderManager == null) {
Slog.e(TAG, "Failed to find data loader manager service");
return false;
}
- if (!dataLoaderManager.initializeDataLoader(mountId, dataLoaderParams, listener)) {
+ if (!dataLoaderManager.initializeDataLoader(mountId, params, control, listener)) {
Slog.e(TAG, "Failed to initialize data loader");
return false;
}
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 4fc9e90..ad20d38 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -22,12 +22,13 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderManager;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -65,26 +66,22 @@
final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
@Override
- public boolean initializeDataLoader(int dataLoaderId, Bundle params,
- IDataLoaderStatusListener listener) {
+ public boolean initializeDataLoader(int dataLoaderId, DataLoaderParamsParcel params,
+ FileSystemControlParcel control, IDataLoaderStatusListener listener) {
synchronized (mLock) {
if (mServiceConnections.get(dataLoaderId) != null) {
Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists.");
return false;
}
}
- ComponentName componentName = params.getParcelable("componentName");
- if (componentName == null) {
- Slog.e(TAG, "Must specify component name.");
- return false;
- }
+ ComponentName componentName = new ComponentName(params.packageName, params.className);
ComponentName dataLoaderComponent = resolveDataLoaderComponentName(componentName);
if (dataLoaderComponent == null) {
return false;
}
// Binds to the specific data loader service
DataLoaderServiceConnection connection =
- new DataLoaderServiceConnection(dataLoaderId, params, listener);
+ new DataLoaderServiceConnection(dataLoaderId, params, control, listener);
Intent intent = new Intent();
intent.setComponent(dataLoaderComponent);
if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
@@ -181,13 +178,16 @@
class DataLoaderServiceConnection implements ServiceConnection {
final int mId;
- final Bundle mParams;
+ final DataLoaderParamsParcel mParams;
+ final FileSystemControlParcel mControl;
final IDataLoaderStatusListener mListener;
IDataLoader mDataLoader;
- DataLoaderServiceConnection(int id, Bundle params, IDataLoaderStatusListener listener) {
+ DataLoaderServiceConnection(int id, DataLoaderParamsParcel params,
+ FileSystemControlParcel control, IDataLoaderStatusListener listener) {
mId = id;
mParams = params;
+ mControl = control;
mListener = listener;
mDataLoader = null;
}
@@ -199,7 +199,7 @@
mServiceConnections.append(mId, this);
}
try {
- mDataLoader.create(mId, mParams, mListener);
+ mDataLoader.create(mId, mParams, mControl, mListener);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to create data loader service.", e);
}
@@ -226,7 +226,6 @@
synchronized (mLock) {
mServiceConnections.remove(mId);
}
- mParams.clear();
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5cc5059..97aa79d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -69,6 +69,7 @@
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
import android.content.pm.InstallationFile;
+import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -1052,9 +1053,9 @@
IPackageInstallerSessionFileSystemConnector.Stub {
final Set<String> mAddedFiles = new ArraySet<>();
- FileSystemConnector(List<InstallationFile> addedFiles) {
- for (InstallationFile file : addedFiles) {
- mAddedFiles.add(file.getName());
+ FileSystemConnector(List<InstallationFileParcel> addedFiles) {
+ for (InstallationFileParcel file : addedFiles) {
+ mAddedFiles.add(file.name);
}
}
@@ -2444,14 +2445,14 @@
return true;
}
- final List<InstallationFile> addedFiles = new ArrayList<>(mFiles.size());
+ final List<InstallationFileParcel> addedFiles = new ArrayList<>();
+ final List<String> removedFiles = new ArrayList<>();
+
for (InstallationFile file : mFiles) {
if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
- addedFiles.add(file);
+ addedFiles.add(file.getData());
+ continue;
}
- }
- final List<String> removedFiles = new ArrayList<>(mFiles.size());
- for (InstallationFile file : mFiles) {
if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) {
String name = file.getName().substring(
0, file.getName().length() - REMOVE_MARKER_EXTENSION.length());
@@ -2494,7 +2495,10 @@
break;
}
case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
- dataLoader.prepareImage(addedFiles, removedFiles);
+ dataLoader.prepareImage(
+ addedFiles.toArray(
+ new InstallationFileParcel[addedFiles.size()]),
+ removedFiles.toArray(new String[removedFiles.size()]));
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
@@ -2547,13 +2551,8 @@
control.callback = connector;
final DataLoaderParams params = this.params.dataLoaderParams;
-
- Bundle dataLoaderParams = new Bundle();
- dataLoaderParams.putParcelable("componentName", params.getComponentName());
- dataLoaderParams.putParcelable("control", control);
- dataLoaderParams.putParcelable("params", params.getData());
-
- if (!dataLoaderManager.initializeDataLoader(sessionId, dataLoaderParams, listener)) {
+ if (!dataLoaderManager.initializeDataLoader(
+ sessionId, params.getData(), control, listener)) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to initialize data loader");
}