Merge "CompatChanges for TileService#startActivityAndCollapse"

This commit is contained in:
Rasheed Lewis
2023-01-10 16:35:06 +00:00
committed by Android (Google) Code Review
4 changed files with 126 additions and 18 deletions

View File

@@ -40439,7 +40439,7 @@ package android.service.quicksettings {
method public void onTileRemoved();
method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
method public final void showDialog(android.app.Dialog);
method public final void startActivityAndCollapse(android.content.Intent);
method @Deprecated public final void startActivityAndCollapse(android.content.Intent);
method public final void startActivityAndCollapse(@NonNull android.app.PendingIntent);
method public final void unlockAndRun(Runnable);
field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";

View File

@@ -24,6 +24,9 @@ import android.app.Dialog;
import android.app.PendingIntent;
import android.app.Service;
import android.app.StatusBarManager;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -166,6 +169,17 @@ public class TileService extends Service {
*/
public static final String EXTRA_STATE = "state";
/**
* The method {@link TileService#startActivityAndCollapse(Intent)} will verify that only
* apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or higher will
* not be allowed to use it.
*
* @hide
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static final long START_ACTIVITY_NEEDS_PENDING_INTENT = 241766793L;
private final H mHandler = new H(Looper.getMainLooper());
private boolean mListening = false;
@@ -251,7 +265,6 @@ public class TileService extends Service {
* This will collapse the Quick Settings panel and show the dialog.
*
* @param dialog Dialog to show.
*
* @see #isLocked()
*/
public final void showDialog(Dialog dialog) {
@@ -330,8 +343,19 @@ public class TileService extends Service {
/**
* Start an activity while collapsing the panel.
*
* @deprecated for versions {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and up,
* use {@link TileService#startActivityAndCollapse(PendingIntent)} instead.
* @throws UnsupportedOperationException if called in versions
* {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and up
*/
@Deprecated
public final void startActivityAndCollapse(Intent intent) {
if (CompatChanges.isChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT)) {
throw new UnsupportedOperationException(
"startActivityAndCollapse: Starting activity from TileService using an Intent"
+ " is not allowed.");
}
startActivity(intent);
try {
mService.onStartActivity(mTileToken);
@@ -410,7 +434,7 @@ public class TileService extends Service {
}
@Override
public void onUnlockComplete() throws RemoteException{
public void onUnlockComplete() throws RemoteException {
mHandler.sendEmptyMessage(H.MSG_UNLOCK_COMPLETE);
}
};

View File

@@ -15,6 +15,9 @@
*/
package com.android.systemui.qs.external;
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -41,15 +44,15 @@ import androidx.annotation.WorkerThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
/**
* Manages the lifecycle of a TileService.
* <p>
@@ -124,7 +127,9 @@ public class TileLifecycleManager extends BroadcastReceiver implements
/** Injectable factory for TileLifecycleManager. */
@AssistedFactory
public interface Factory {
/** */
/**
*
*/
TileLifecycleManager create(Intent intent, UserHandle userHandle);
}
@@ -161,7 +166,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
* Determines whether the associated TileService is a Boolean Tile.
*
* @return true if {@link TileService#META_DATA_TOGGLEABLE_TILE} is set to {@code true} for this
* tile
* tile
* @see TileService#META_DATA_TOGGLEABLE_TILE
*/
public boolean isToggleableTile() {
@@ -207,12 +212,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser);
mBindTryCount++;
try {
mIsBound = mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
| Context.BIND_WAIVE_PRIORITY,
mUser);
mIsBound = bindServices();
if (!mIsBound) {
mContext.unbindService(this);
}
@@ -237,6 +237,24 @@ public class TileLifecycleManager extends BroadcastReceiver implements
}
}
private boolean bindServices() {
String packageName = mIntent.getComponent().getPackageName();
if (CompatChanges.isChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, packageName,
mUser)) {
return mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_WAIVE_PRIORITY,
mUser);
}
return mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
| Context.BIND_WAIVE_PRIORITY,
mUser);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Log.d(TAG, "onServiceConnected " + name);
@@ -418,8 +436,11 @@ public class TileLifecycleManager extends BroadcastReceiver implements
mPackageManagerAdapter.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier());
return true;
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Package not available: " + packageName, e);
else Log.d(TAG, "Package not available: " + packageName);
if (DEBUG) {
Log.d(TAG, "Package not available: " + packageName, e);
} else {
Log.d(TAG, "Package not available: " + packageName);
}
}
return false;
}

View File

@@ -15,11 +15,17 @@
*/
package com.android.systemui.qs.external;
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -29,6 +35,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -59,6 +66,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.MockitoSession;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -77,11 +85,16 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
private Handler mHandler;
private TileLifecycleManager mStateManager;
private TestContextWrapper mWrappedContext;
private MockitoSession mMockitoSession;
@Before
public void setUp() throws Exception {
setPackageEnabled(true);
mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class");
mMockitoSession = mockitoSession()
.initMocks(this)
.mockStatic(CompatChanges.class)
.startMocking();
// Stub.asInterface will just return itself.
when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
@@ -106,7 +119,13 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
@After
public void tearDown() throws Exception {
mThread.quit();
if (mMockitoSession != null) {
mMockitoSession.finishMocking();
}
if (mThread != null) {
mThread.quit();
}
mStateManager.handleDestroy();
}
@@ -290,6 +309,50 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
verify(falseContext).unbindService(captor.getValue());
}
@Test
public void testVersionUDoesNotBindsAllowBackgroundActivity() {
mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, true);
Context falseContext = mock(Context.class);
TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser);
manager.setBindService(true);
int flags = Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_WAIVE_PRIORITY;
verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
}
@Test
public void testVersionLessThanUBindsAllowBackgroundActivity() {
mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, false);
Context falseContext = mock(Context.class);
TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser);
manager.setBindService(true);
int flags = Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
| Context.BIND_WAIVE_PRIORITY;
verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
}
private void mockChangeEnabled(long changeId, boolean enabled) {
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
any(UserHandle.class)));
}
private static class TestContextWrapper extends ContextWrapper {
private IntentFilter mLastIntentFilter;
private int mLastFlag;