Loading services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +49 −44 Original line number Original line Diff line number Diff line Loading @@ -3001,64 +3001,64 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } } } // Select which transport to use for the next backup operation. /** Selects transport {@code transportName} and returns previous selected transport. */ @Override @Override public String selectBackupTransport(String transport) { public String selectBackupTransport(String transportName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, mContext.enforceCallingOrSelfPermission( "selectBackupTransport"); android.Manifest.permission.BACKUP, "selectBackupTransport"); final long oldId = Binder.clearCallingIdentity(); final long oldId = Binder.clearCallingIdentity(); try { try { String prevTransport = mTransportManager.selectTransport(transport); String previousTransportName = mTransportManager.selectTransport(transportName); updateStateForTransport(transport); updateStateForTransport(transportName); Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName() Slog.v(TAG, "selectBackupTransport(transport = " + transportName + " returning " + prevTransport); + "): previous transport = " + previousTransportName); return prevTransport; return previousTransportName; } finally { } finally { Binder.restoreCallingIdentity(oldId); Binder.restoreCallingIdentity(oldId); } } } } @Override @Override public void selectBackupTransportAsync(final ComponentName transport, public void selectBackupTransportAsync( final ISelectBackupTransportCallback listener) { ComponentName transportComponent, ISelectBackupTransportCallback listener) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, mContext.enforceCallingOrSelfPermission( "selectBackupTransportAsync"); android.Manifest.permission.BACKUP, "selectBackupTransportAsync"); final long oldId = Binder.clearCallingIdentity(); final long oldId = Binder.clearCallingIdentity(); Slog.v(TAG, "selectBackupTransportAsync() called with transport " + transport.flattenToShortString()); mTransportManager.ensureTransportReady(transport, new TransportManager.TransportReadyCallback() { @Override public void onSuccess(String transportName) { mTransportManager.selectTransport(transportName); updateStateForTransport(mTransportManager.getCurrentTransportName()); Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString()); try { try { listener.onSuccess(transportName); String transportString = transportComponent.flattenToShortString(); } catch (RemoteException e) { Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")"); // Nothing to do here. mBackupHandler.post( () -> { String transportName = null; int result = mTransportManager.registerAndSelectTransport(transportComponent); if (result == BackupManager.SUCCESS) { try { transportName = mTransportManager.getTransportName(transportComponent); updateStateForTransport(transportName); } catch (TransportNotRegisteredException e) { Slog.e(TAG, "Transport got unregistered"); result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } } } @Override public void onFailure(int reason) { Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString()); try { try { listener.onFailure(reason); if (transportName != null) { } catch (RemoteException e) { listener.onSuccess(transportName); // Nothing to do here. } else { listener.onFailure(result); } } } catch (RemoteException e) { Slog.e(TAG, "ISelectBackupTransportCallback listener not available"); } } }); }); } finally { Binder.restoreCallingIdentity(oldId); Binder.restoreCallingIdentity(oldId); } } } private void updateStateForTransport(String newTransportName) { private void updateStateForTransport(String newTransportName) { // Publish the name change // Publish the name change Loading @@ -3066,18 +3066,23 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter Settings.Secure.BACKUP_TRANSPORT, newTransportName); Settings.Secure.BACKUP_TRANSPORT, newTransportName); // And update our current-dataset bookkeeping // And update our current-dataset bookkeeping IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName); String callerLogString = "BMS.updateStateForTransport()"; if (transport != null) { TransportClient transportClient = mTransportManager.getTransportClient(newTransportName, callerLogString); if (transportClient != null) { try { try { IBackupTransport transport = transportClient.connectOrThrow(callerLogString); mCurrentToken = transport.getCurrentRestoreSet(); mCurrentToken = transport.getCurrentRestoreSet(); } catch (Exception e) { } catch (Exception e) { // Oops. We can't know the current dataset token, so reset and figure it out // Oops. We can't know the current dataset token, so reset and figure it out // when we do the next k/v backup operation on this transport. // when we do the next k/v backup operation on this transport. mCurrentToken = 0; mCurrentToken = 0; Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0"); } } } else { } else { // The named transport isn't bound at this particular moment, so we can't Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0"); // know yet what its current dataset token is. Reset as above. // The named transport isn't registered, so we can't know what its current dataset token // is. Reset as above. mCurrentToken = 0; mCurrentToken = 0; } } } } Loading services/backup/java/com/android/server/backup/TransportManager.java +104 −87 Original line number Original line Diff line number Diff line Loading @@ -112,23 +112,6 @@ public class TransportManager { @GuardedBy("mTransportLock") @GuardedBy("mTransportLock") private volatile String mCurrentTransportName; private volatile String mCurrentTransportName; /** * Callback interface for {@link #ensureTransportReady(ComponentName, TransportReadyCallback)}. */ public interface TransportReadyCallback { /** * Will be called when the transport is ready. */ void onSuccess(String transportName); /** * Will be called when it's not possible to make transport ready. */ void onFailure(int reason); } TransportManager( TransportManager( Context context, Context context, Set<ComponentName> whitelist, Set<ComponentName> whitelist, Loading Loading @@ -239,6 +222,17 @@ public class TransportManager { return getTransportBinder(mCurrentTransportName); return getTransportBinder(mCurrentTransportName); } } /** * Returns the transport name associated with {@code transportComponent}. * @throws TransportNotRegisteredException if the transport is not registered. */ public String getTransportName(ComponentName transportComponent) throws TransportNotRegisteredException { synchronized (mTransportLock) { return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name; } } /** /** * Retrieve the configuration intent of {@code transportName}. * Retrieve the configuration intent of {@code transportName}. * @throws TransportNotRegisteredException if the transport is not registered. * @throws TransportNotRegisteredException if the transport is not registered. Loading Loading @@ -340,23 +334,6 @@ public class TransportManager { return null; return null; } } /** * Returns the transport name associated with {@param transportComponent} or {@code null} if not * found. */ @Nullable public String getTransportName(ComponentName transportComponent) { synchronized (mTransportLock) { TransportDescription description = mRegisteredTransportsDescriptionMap.get(transportComponent); if (description == null) { Slog.e(TAG, "Trying to find name of unregistered transport " + transportComponent); return null; } return description.name; } } @GuardedBy("mTransportLock") @GuardedBy("mTransportLock") @Nullable @Nullable private ComponentName getRegisteredTransportComponentLocked(String transportName) { private ComponentName getRegisteredTransportComponentLocked(String transportName) { Loading Loading @@ -552,29 +529,6 @@ public class TransportManager { return mCurrentTransportName; return mCurrentTransportName; } } String selectTransport(String transport) { synchronized (mTransportLock) { String prevTransport = mCurrentTransportName; mCurrentTransportName = transport; return prevTransport; } } void ensureTransportReady(ComponentName transportComponent, TransportReadyCallback listener) { synchronized (mTransportLock) { TransportConnection conn = mValidTransports.get(transportComponent); if (conn == null) { listener.onFailure(BackupManager.ERROR_TRANSPORT_UNAVAILABLE); return; } // Transport can be unbound if the process hosting it crashed. conn.bindIfUnbound(); conn.addListener(listener); } } // This is for mocking, Mockito can't mock if package-protected and in the same package but // This is for mocking, Mockito can't mock if package-protected and in the same package but // different class loaders. Checked with the debugger and class loaders are different // different class loaders. Checked with the debugger and class loaders are different // See https://github.com/mockito/mockito/issues/796 // See https://github.com/mockito/mockito/issues/796 Loading Loading @@ -674,6 +628,90 @@ public class TransportManager { createSystemUserHandle()); createSystemUserHandle()); } } String selectTransport(String transportName) { synchronized (mTransportLock) { String prevTransport = mCurrentTransportName; mCurrentTransportName = transportName; return prevTransport; } } /** * Tries to register the transport if not registered. If successful also selects the transport. * * @param transportComponent Host of the transport. * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ public int registerAndSelectTransport(ComponentName transportComponent) { synchronized (mTransportLock) { if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) { int result = registerTransport(transportComponent); if (result != BackupManager.SUCCESS) { return result; } } try { selectTransport(getTransportName(transportComponent)); return BackupManager.SUCCESS; } catch (TransportNotRegisteredException e) { // Shouldn't happen because we are holding the lock Slog.wtf(TAG, "Transport unexpectedly not registered"); return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } } /** * Tries to register transport represented by {@code transportComponent}. * * @param transportComponent Host of the transport that we want to register. * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ private int registerTransport(ComponentName transportComponent) { String transportString = transportComponent.flattenToShortString(); String callerLogString = "TransportManager.registerTransport()"; TransportClient transportClient = mTransportClientManager.getTransportClient(transportComponent, callerLogString); final IBackupTransport transport; try { transport = transportClient.connectOrThrow(callerLogString); } catch (TransportNotAvailableException e) { Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration"); mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1); int result; if (isTransportValid(transport)) { try { registerTransport(transportComponent, transport); // If registerTransport() hasn't thrown... result = BackupManager.SUCCESS; } catch (RemoteException e) { Slog.e(TAG, "Transport " + transportString + " died while registering"); result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } else { Slog.w(TAG, "Can't register invalid transport " + transportString); result = BackupManager.ERROR_TRANSPORT_INVALID; } mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); if (result == BackupManager.SUCCESS) { Slog.d(TAG, "Transport " + transportString + " registered"); } else { EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0); } return result; } /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ private void registerTransport(ComponentName transportComponent, IBackupTransport transport) private void registerTransport(ComponentName transportComponent, IBackupTransport transport) throws RemoteException { throws RemoteException { Loading @@ -690,11 +728,18 @@ public class TransportManager { } } } } private boolean isTransportValid(IBackupTransport transport) { if (mTransportBoundListener == null) { Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid"); return false; } return mTransportBoundListener.onTransportBound(transport); } private class TransportConnection implements ServiceConnection { private class TransportConnection implements ServiceConnection { // Hold mTransportLock to access these fields so as to provide a consistent view of them. // Hold mTransportLock to access these fields so as to provide a consistent view of them. private volatile IBackupTransport mBinder; private volatile IBackupTransport mBinder; private final List<TransportReadyCallback> mListeners = new ArrayList<>(); private volatile String mTransportName; private volatile String mTransportName; private final ComponentName mTransportComponent; private final ComponentName mTransportComponent; Loading @@ -716,15 +761,7 @@ public class TransportManager { mTransportName = mBinder.name(); mTransportName = mBinder.name(); // BackupManager requests some fields from the transport. If they are // BackupManager requests some fields from the transport. If they are // invalid, throw away this transport. // invalid, throw away this transport. final boolean valid; if (isTransportValid(mBinder)) { if (mTransportBoundListener != null) { valid = mTransportBoundListener.onTransportBound(mBinder); } else { Slog.w(TAG, "setTransportBoundListener() not called, assuming transport " + component + " valid"); valid = true; } if (valid) { // We're now using the always-bound connection to do the registration but // We're now using the always-bound connection to do the registration but // when we remove the always-bound code this will be in the first binding // when we remove the always-bound code this will be in the first binding // TODO: Move registration to first binding // TODO: Move registration to first binding Loading @@ -742,9 +779,6 @@ public class TransportManager { if (success) { if (success) { Slog.d(TAG, "Bound to transport: " + componentShortString); Slog.d(TAG, "Bound to transport: " + componentShortString); mBoundTransports.put(mTransportName, component); mBoundTransports.put(mTransportName, component); for (TransportReadyCallback listener : mListeners) { listener.onSuccess(mTransportName); } // cancel rebinding on timeout for this component as we've already connected // cancel rebinding on timeout for this component as we've already connected mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); } else { } else { Loading @@ -756,11 +790,7 @@ public class TransportManager { mValidTransports.remove(component); mValidTransports.remove(component); mEligibleTransports.remove(component); mEligibleTransports.remove(component); mBinder = null; mBinder = null; for (TransportReadyCallback listener : mListeners) { listener.onFailure(BackupManager.ERROR_TRANSPORT_INVALID); } } } mListeners.clear(); } } } } } } Loading Loading @@ -815,19 +845,6 @@ public class TransportManager { } } } } private void addListener(TransportReadyCallback listener) { synchronized (mTransportLock) { if (mBinder == null) { // We are waiting for bind to complete. If mBinder is set to null after the bind // is complete due to transport being invalid, we won't find 'this' connection // object in mValidTransports list and this function can't be called. mListeners.add(listener); } else { listener.onSuccess(mTransportName); } } } private long getRebindTimeout() { private long getRebindTimeout() { final boolean isDeviceProvisioned = Settings.Global.getInt( final boolean isDeviceProvisioned = Settings.Global.getInt( mContext.getContentResolver(), mContext.getContentResolver(), Loading services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +2 −9 Original line number Original line Diff line number Diff line Loading @@ -560,16 +560,9 @@ public class PerformBackupTask implements BackupRestoreTask { } } backupManagerService.addBackupTrace("init required; rerunning"); backupManagerService.addBackupTrace("init required; rerunning"); try { try { final String name = backupManagerService.getTransportManager() String name = backupManagerService.getTransportManager() .getTransportName(mTransportClient.getTransportComponent()); .getTransportName(mTransportClient.getTransportComponent()); if (name != null) { backupManagerService.getPendingInits().add(name); backupManagerService.getPendingInits().add(name); } else { if (DEBUG) { Slog.w(TAG, "Couldn't find name of transport " + mTransportClient.getTransportComponent() + " for init"); } } } catch (Exception e) { } catch (Exception e) { Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage()); Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage()); // swallow it and proceed; we don't rely on this // swallow it and proceed; we don't rely on this Loading services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java +131 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes services/robotests/src/com/android/server/backup/TransportManagerTest.java +0 −58 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; import static org.testng.Assert.expectThrows; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.ComponentName; import android.content.Intent; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; Loading @@ -40,7 +39,6 @@ import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.FrameworkRobolectricTestRunner; Loading Loading @@ -87,9 +85,6 @@ public class TransportManagerTest { private final TransportBoundListenerStub mTransportBoundListenerStub = private final TransportBoundListenerStub mTransportBoundListenerStub = new TransportBoundListenerStub(true); new TransportBoundListenerStub(true); private final TransportReadyCallbackStub mTransportReadyCallbackStub = new TransportReadyCallbackStub(); @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); Loading Loading @@ -465,59 +460,6 @@ public class TransportManagerTest { assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name); assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name); } } @Test public void ensureTransportReady_transportNotYetBound_callsListenerOnFailure() throws Exception { setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); TransportManager transportManager = new TransportManager( RuntimeEnvironment.application.getApplicationContext(), new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)), mTransport1.name, mTransportBoundListenerStub, ShadowLooper.getMainLooper()); transportManager.ensureTransportReady(mTransport1.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty(); assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn( Collections.singleton( BackupManager.ERROR_TRANSPORT_UNAVAILABLE)); } @Test public void ensureTransportReady_transportCannotBeBound_callsListenerOnFailure() throws Exception { TransportManager transportManager = createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); transportManager.ensureTransportReady(mTransport1.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty(); assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn( Collections.singleton( BackupManager.ERROR_TRANSPORT_UNAVAILABLE)); } @Test public void ensureTransportReady_transportsAlreadyBound_callsListenerOnSuccess() throws Exception { TransportManager transportManager = createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); transportManager.ensureTransportReady(mTransport2.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).containsExactlyElementsIn( Collections.singleton(mTransport2.name)); assertThat(mTransportReadyCallbackStub.getFailureCalls()).isEmpty(); } @Test @Test public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception { public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception { TransportManager transportManager = TransportManager transportManager = Loading Loading
services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +49 −44 Original line number Original line Diff line number Diff line Loading @@ -3001,64 +3001,64 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } } } // Select which transport to use for the next backup operation. /** Selects transport {@code transportName} and returns previous selected transport. */ @Override @Override public String selectBackupTransport(String transport) { public String selectBackupTransport(String transportName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, mContext.enforceCallingOrSelfPermission( "selectBackupTransport"); android.Manifest.permission.BACKUP, "selectBackupTransport"); final long oldId = Binder.clearCallingIdentity(); final long oldId = Binder.clearCallingIdentity(); try { try { String prevTransport = mTransportManager.selectTransport(transport); String previousTransportName = mTransportManager.selectTransport(transportName); updateStateForTransport(transport); updateStateForTransport(transportName); Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName() Slog.v(TAG, "selectBackupTransport(transport = " + transportName + " returning " + prevTransport); + "): previous transport = " + previousTransportName); return prevTransport; return previousTransportName; } finally { } finally { Binder.restoreCallingIdentity(oldId); Binder.restoreCallingIdentity(oldId); } } } } @Override @Override public void selectBackupTransportAsync(final ComponentName transport, public void selectBackupTransportAsync( final ISelectBackupTransportCallback listener) { ComponentName transportComponent, ISelectBackupTransportCallback listener) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, mContext.enforceCallingOrSelfPermission( "selectBackupTransportAsync"); android.Manifest.permission.BACKUP, "selectBackupTransportAsync"); final long oldId = Binder.clearCallingIdentity(); final long oldId = Binder.clearCallingIdentity(); Slog.v(TAG, "selectBackupTransportAsync() called with transport " + transport.flattenToShortString()); mTransportManager.ensureTransportReady(transport, new TransportManager.TransportReadyCallback() { @Override public void onSuccess(String transportName) { mTransportManager.selectTransport(transportName); updateStateForTransport(mTransportManager.getCurrentTransportName()); Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString()); try { try { listener.onSuccess(transportName); String transportString = transportComponent.flattenToShortString(); } catch (RemoteException e) { Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")"); // Nothing to do here. mBackupHandler.post( () -> { String transportName = null; int result = mTransportManager.registerAndSelectTransport(transportComponent); if (result == BackupManager.SUCCESS) { try { transportName = mTransportManager.getTransportName(transportComponent); updateStateForTransport(transportName); } catch (TransportNotRegisteredException e) { Slog.e(TAG, "Transport got unregistered"); result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } } } @Override public void onFailure(int reason) { Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString()); try { try { listener.onFailure(reason); if (transportName != null) { } catch (RemoteException e) { listener.onSuccess(transportName); // Nothing to do here. } else { listener.onFailure(result); } } } catch (RemoteException e) { Slog.e(TAG, "ISelectBackupTransportCallback listener not available"); } } }); }); } finally { Binder.restoreCallingIdentity(oldId); Binder.restoreCallingIdentity(oldId); } } } private void updateStateForTransport(String newTransportName) { private void updateStateForTransport(String newTransportName) { // Publish the name change // Publish the name change Loading @@ -3066,18 +3066,23 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter Settings.Secure.BACKUP_TRANSPORT, newTransportName); Settings.Secure.BACKUP_TRANSPORT, newTransportName); // And update our current-dataset bookkeeping // And update our current-dataset bookkeeping IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName); String callerLogString = "BMS.updateStateForTransport()"; if (transport != null) { TransportClient transportClient = mTransportManager.getTransportClient(newTransportName, callerLogString); if (transportClient != null) { try { try { IBackupTransport transport = transportClient.connectOrThrow(callerLogString); mCurrentToken = transport.getCurrentRestoreSet(); mCurrentToken = transport.getCurrentRestoreSet(); } catch (Exception e) { } catch (Exception e) { // Oops. We can't know the current dataset token, so reset and figure it out // Oops. We can't know the current dataset token, so reset and figure it out // when we do the next k/v backup operation on this transport. // when we do the next k/v backup operation on this transport. mCurrentToken = 0; mCurrentToken = 0; Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0"); } } } else { } else { // The named transport isn't bound at this particular moment, so we can't Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0"); // know yet what its current dataset token is. Reset as above. // The named transport isn't registered, so we can't know what its current dataset token // is. Reset as above. mCurrentToken = 0; mCurrentToken = 0; } } } } Loading
services/backup/java/com/android/server/backup/TransportManager.java +104 −87 Original line number Original line Diff line number Diff line Loading @@ -112,23 +112,6 @@ public class TransportManager { @GuardedBy("mTransportLock") @GuardedBy("mTransportLock") private volatile String mCurrentTransportName; private volatile String mCurrentTransportName; /** * Callback interface for {@link #ensureTransportReady(ComponentName, TransportReadyCallback)}. */ public interface TransportReadyCallback { /** * Will be called when the transport is ready. */ void onSuccess(String transportName); /** * Will be called when it's not possible to make transport ready. */ void onFailure(int reason); } TransportManager( TransportManager( Context context, Context context, Set<ComponentName> whitelist, Set<ComponentName> whitelist, Loading Loading @@ -239,6 +222,17 @@ public class TransportManager { return getTransportBinder(mCurrentTransportName); return getTransportBinder(mCurrentTransportName); } } /** * Returns the transport name associated with {@code transportComponent}. * @throws TransportNotRegisteredException if the transport is not registered. */ public String getTransportName(ComponentName transportComponent) throws TransportNotRegisteredException { synchronized (mTransportLock) { return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name; } } /** /** * Retrieve the configuration intent of {@code transportName}. * Retrieve the configuration intent of {@code transportName}. * @throws TransportNotRegisteredException if the transport is not registered. * @throws TransportNotRegisteredException if the transport is not registered. Loading Loading @@ -340,23 +334,6 @@ public class TransportManager { return null; return null; } } /** * Returns the transport name associated with {@param transportComponent} or {@code null} if not * found. */ @Nullable public String getTransportName(ComponentName transportComponent) { synchronized (mTransportLock) { TransportDescription description = mRegisteredTransportsDescriptionMap.get(transportComponent); if (description == null) { Slog.e(TAG, "Trying to find name of unregistered transport " + transportComponent); return null; } return description.name; } } @GuardedBy("mTransportLock") @GuardedBy("mTransportLock") @Nullable @Nullable private ComponentName getRegisteredTransportComponentLocked(String transportName) { private ComponentName getRegisteredTransportComponentLocked(String transportName) { Loading Loading @@ -552,29 +529,6 @@ public class TransportManager { return mCurrentTransportName; return mCurrentTransportName; } } String selectTransport(String transport) { synchronized (mTransportLock) { String prevTransport = mCurrentTransportName; mCurrentTransportName = transport; return prevTransport; } } void ensureTransportReady(ComponentName transportComponent, TransportReadyCallback listener) { synchronized (mTransportLock) { TransportConnection conn = mValidTransports.get(transportComponent); if (conn == null) { listener.onFailure(BackupManager.ERROR_TRANSPORT_UNAVAILABLE); return; } // Transport can be unbound if the process hosting it crashed. conn.bindIfUnbound(); conn.addListener(listener); } } // This is for mocking, Mockito can't mock if package-protected and in the same package but // This is for mocking, Mockito can't mock if package-protected and in the same package but // different class loaders. Checked with the debugger and class loaders are different // different class loaders. Checked with the debugger and class loaders are different // See https://github.com/mockito/mockito/issues/796 // See https://github.com/mockito/mockito/issues/796 Loading Loading @@ -674,6 +628,90 @@ public class TransportManager { createSystemUserHandle()); createSystemUserHandle()); } } String selectTransport(String transportName) { synchronized (mTransportLock) { String prevTransport = mCurrentTransportName; mCurrentTransportName = transportName; return prevTransport; } } /** * Tries to register the transport if not registered. If successful also selects the transport. * * @param transportComponent Host of the transport. * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ public int registerAndSelectTransport(ComponentName transportComponent) { synchronized (mTransportLock) { if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) { int result = registerTransport(transportComponent); if (result != BackupManager.SUCCESS) { return result; } } try { selectTransport(getTransportName(transportComponent)); return BackupManager.SUCCESS; } catch (TransportNotRegisteredException e) { // Shouldn't happen because we are holding the lock Slog.wtf(TAG, "Transport unexpectedly not registered"); return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } } /** * Tries to register transport represented by {@code transportComponent}. * * @param transportComponent Host of the transport that we want to register. * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ private int registerTransport(ComponentName transportComponent) { String transportString = transportComponent.flattenToShortString(); String callerLogString = "TransportManager.registerTransport()"; TransportClient transportClient = mTransportClientManager.getTransportClient(transportComponent, callerLogString); final IBackupTransport transport; try { transport = transportClient.connectOrThrow(callerLogString); } catch (TransportNotAvailableException e) { Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration"); mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1); int result; if (isTransportValid(transport)) { try { registerTransport(transportComponent, transport); // If registerTransport() hasn't thrown... result = BackupManager.SUCCESS; } catch (RemoteException e) { Slog.e(TAG, "Transport " + transportString + " died while registering"); result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } else { Slog.w(TAG, "Can't register invalid transport " + transportString); result = BackupManager.ERROR_TRANSPORT_INVALID; } mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); if (result == BackupManager.SUCCESS) { Slog.d(TAG, "Transport " + transportString + " registered"); } else { EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0); } return result; } /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ private void registerTransport(ComponentName transportComponent, IBackupTransport transport) private void registerTransport(ComponentName transportComponent, IBackupTransport transport) throws RemoteException { throws RemoteException { Loading @@ -690,11 +728,18 @@ public class TransportManager { } } } } private boolean isTransportValid(IBackupTransport transport) { if (mTransportBoundListener == null) { Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid"); return false; } return mTransportBoundListener.onTransportBound(transport); } private class TransportConnection implements ServiceConnection { private class TransportConnection implements ServiceConnection { // Hold mTransportLock to access these fields so as to provide a consistent view of them. // Hold mTransportLock to access these fields so as to provide a consistent view of them. private volatile IBackupTransport mBinder; private volatile IBackupTransport mBinder; private final List<TransportReadyCallback> mListeners = new ArrayList<>(); private volatile String mTransportName; private volatile String mTransportName; private final ComponentName mTransportComponent; private final ComponentName mTransportComponent; Loading @@ -716,15 +761,7 @@ public class TransportManager { mTransportName = mBinder.name(); mTransportName = mBinder.name(); // BackupManager requests some fields from the transport. If they are // BackupManager requests some fields from the transport. If they are // invalid, throw away this transport. // invalid, throw away this transport. final boolean valid; if (isTransportValid(mBinder)) { if (mTransportBoundListener != null) { valid = mTransportBoundListener.onTransportBound(mBinder); } else { Slog.w(TAG, "setTransportBoundListener() not called, assuming transport " + component + " valid"); valid = true; } if (valid) { // We're now using the always-bound connection to do the registration but // We're now using the always-bound connection to do the registration but // when we remove the always-bound code this will be in the first binding // when we remove the always-bound code this will be in the first binding // TODO: Move registration to first binding // TODO: Move registration to first binding Loading @@ -742,9 +779,6 @@ public class TransportManager { if (success) { if (success) { Slog.d(TAG, "Bound to transport: " + componentShortString); Slog.d(TAG, "Bound to transport: " + componentShortString); mBoundTransports.put(mTransportName, component); mBoundTransports.put(mTransportName, component); for (TransportReadyCallback listener : mListeners) { listener.onSuccess(mTransportName); } // cancel rebinding on timeout for this component as we've already connected // cancel rebinding on timeout for this component as we've already connected mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); } else { } else { Loading @@ -756,11 +790,7 @@ public class TransportManager { mValidTransports.remove(component); mValidTransports.remove(component); mEligibleTransports.remove(component); mEligibleTransports.remove(component); mBinder = null; mBinder = null; for (TransportReadyCallback listener : mListeners) { listener.onFailure(BackupManager.ERROR_TRANSPORT_INVALID); } } } mListeners.clear(); } } } } } } Loading Loading @@ -815,19 +845,6 @@ public class TransportManager { } } } } private void addListener(TransportReadyCallback listener) { synchronized (mTransportLock) { if (mBinder == null) { // We are waiting for bind to complete. If mBinder is set to null after the bind // is complete due to transport being invalid, we won't find 'this' connection // object in mValidTransports list and this function can't be called. mListeners.add(listener); } else { listener.onSuccess(mTransportName); } } } private long getRebindTimeout() { private long getRebindTimeout() { final boolean isDeviceProvisioned = Settings.Global.getInt( final boolean isDeviceProvisioned = Settings.Global.getInt( mContext.getContentResolver(), mContext.getContentResolver(), Loading
services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +2 −9 Original line number Original line Diff line number Diff line Loading @@ -560,16 +560,9 @@ public class PerformBackupTask implements BackupRestoreTask { } } backupManagerService.addBackupTrace("init required; rerunning"); backupManagerService.addBackupTrace("init required; rerunning"); try { try { final String name = backupManagerService.getTransportManager() String name = backupManagerService.getTransportManager() .getTransportName(mTransportClient.getTransportComponent()); .getTransportName(mTransportClient.getTransportComponent()); if (name != null) { backupManagerService.getPendingInits().add(name); backupManagerService.getPendingInits().add(name); } else { if (DEBUG) { Slog.w(TAG, "Couldn't find name of transport " + mTransportClient.getTransportComponent() + " for init"); } } } catch (Exception e) { } catch (Exception e) { Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage()); Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage()); // swallow it and proceed; we don't rely on this // swallow it and proceed; we don't rely on this Loading
services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java +131 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/robotests/src/com/android/server/backup/TransportManagerTest.java +0 −58 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; import static org.testng.Assert.expectThrows; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.ComponentName; import android.content.Intent; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; Loading @@ -40,7 +39,6 @@ import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.FrameworkRobolectricTestRunner; Loading Loading @@ -87,9 +85,6 @@ public class TransportManagerTest { private final TransportBoundListenerStub mTransportBoundListenerStub = private final TransportBoundListenerStub mTransportBoundListenerStub = new TransportBoundListenerStub(true); new TransportBoundListenerStub(true); private final TransportReadyCallbackStub mTransportReadyCallbackStub = new TransportReadyCallbackStub(); @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); Loading Loading @@ -465,59 +460,6 @@ public class TransportManagerTest { assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name); assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name); } } @Test public void ensureTransportReady_transportNotYetBound_callsListenerOnFailure() throws Exception { setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); TransportManager transportManager = new TransportManager( RuntimeEnvironment.application.getApplicationContext(), new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)), mTransport1.name, mTransportBoundListenerStub, ShadowLooper.getMainLooper()); transportManager.ensureTransportReady(mTransport1.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty(); assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn( Collections.singleton( BackupManager.ERROR_TRANSPORT_UNAVAILABLE)); } @Test public void ensureTransportReady_transportCannotBeBound_callsListenerOnFailure() throws Exception { TransportManager transportManager = createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); transportManager.ensureTransportReady(mTransport1.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty(); assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn( Collections.singleton( BackupManager.ERROR_TRANSPORT_UNAVAILABLE)); } @Test public void ensureTransportReady_transportsAlreadyBound_callsListenerOnSuccess() throws Exception { TransportManager transportManager = createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), Collections.singletonList(mTransport1), mTransport1.name); transportManager.ensureTransportReady(mTransport2.componentName, mTransportReadyCallbackStub); assertThat(mTransportReadyCallbackStub.getSuccessCalls()).containsExactlyElementsIn( Collections.singleton(mTransport2.name)); assertThat(mTransportReadyCallbackStub.getFailureCalls()).isEmpty(); } @Test @Test public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception { public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception { TransportManager transportManager = TransportManager transportManager = Loading