Fix crash on Wi-Fi Slice when it's continuously pinned and unpinned

It's because WifiTracker's initialization and onStop is on different
thread. Fine tune the thread logic in SliceBackgroundWorker.

Fixes: 118165942
Test: manual
Change-Id: Icc86b5df7ec3c6fd0e4a79a62ea0c84465e9528d
This commit is contained in:
Jason Chiu
2018-10-23 15:31:54 +08:00
parent 61a8d1fbe4
commit 89b15785c7
4 changed files with 64 additions and 21 deletions

View File

@@ -52,6 +52,7 @@ import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settingslib.SliceBroadcastRelay; import com.android.settingslib.SliceBroadcastRelay;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -134,6 +135,7 @@ public class SettingsSliceProvider extends SliceProvider {
final Set<Uri> mRegisteredUris = new ArraySet<>(); final Set<Uri> mRegisteredUris = new ArraySet<>();
final Map<Uri, SliceBackgroundWorker> mWorkerMap = new ArrayMap<>(); final Map<Uri, SliceBackgroundWorker> mWorkerMap = new ArrayMap<>();
final Set<SliceBackgroundWorker> mLiveWorkers = new ArraySet<>();
public SettingsSliceProvider() { public SettingsSliceProvider() {
super(READ_SEARCH_INDEXABLES); super(READ_SEARCH_INDEXABLES);
@@ -169,7 +171,9 @@ public class SettingsSliceProvider extends SliceProvider {
if (filter != null) { if (filter != null) {
registerIntentToUri(filter, sliceUri); registerIntentToUri(filter, sliceUri);
} }
startBackgroundWorker(sliceable); ThreadUtils.postOnMainThread(() -> {
startBackgroundWorker(sliceable);
});
return; return;
} }
@@ -198,7 +202,9 @@ public class SettingsSliceProvider extends SliceProvider {
SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri); SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri);
mRegisteredUris.remove(sliceUri); mRegisteredUris.remove(sliceUri);
} }
stopBackgroundWorker(sliceUri); ThreadUtils.postOnMainThread(() -> {
stopBackgroundWorker(sliceUri);
});
mSliceDataCache.remove(sliceUri); mSliceDataCache.remove(sliceUri);
} }
@@ -365,12 +371,15 @@ public class SettingsSliceProvider extends SliceProvider {
} }
final Uri uri = sliceable.getUri(); final Uri uri = sliceable.getUri();
Log.d(TAG, "Starting background worker for: " + uri);
if (mWorkerMap.containsKey(uri)) { if (mWorkerMap.containsKey(uri)) {
return; return;
} }
Log.d(TAG, "Starting background worker for: " + uri);
mWorkerMap.put(uri, worker); mWorkerMap.put(uri, worker);
if (!mLiveWorkers.contains(worker)) {
mLiveWorkers.add(worker);
}
worker.onSlicePinned(); worker.onSlicePinned();
} }
@@ -383,6 +392,20 @@ public class SettingsSliceProvider extends SliceProvider {
} }
} }
@Override
public void shutdown() {
for (SliceBackgroundWorker worker : mLiveWorkers) {
ThreadUtils.postOnMainThread(() -> {
try {
worker.close();
} catch (IOException e) {
Log.w(TAG, "Exception when shutting down worker", e);
}
});
}
mLiveWorkers.clear();
}
private List<Uri> buildUrisFromKeys(List<String> keys, String authority) { private List<Uri> buildUrisFromKeys(List<String> keys, String authority) {
final List<Uri> descendants = new ArrayList<>(); final List<Uri> descendants = new ArrayList<>();

View File

@@ -16,9 +16,11 @@
package com.android.settings.slices; package com.android.settings.slices;
import android.annotation.MainThread;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.net.Uri; import android.net.Uri;
import java.io.Closeable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -32,7 +34,7 @@ import java.util.List;
* {@link SliceBackgroundWorker} caches the results, uses the cache to compare if there is any data * {@link SliceBackgroundWorker} caches the results, uses the cache to compare if there is any data
* changed, and then notifies the Slice {@link Uri} to update. * changed, and then notifies the Slice {@link Uri} to update.
*/ */
public abstract class SliceBackgroundWorker<E> { public abstract class SliceBackgroundWorker<E> implements Closeable {
private final ContentResolver mContentResolver; private final ContentResolver mContentResolver;
private final Uri mUri; private final Uri mUri;
@@ -48,12 +50,14 @@ public abstract class SliceBackgroundWorker<E> {
* Called when the Slice is pinned. This is the place to register callbacks or initialize scan * Called when the Slice is pinned. This is the place to register callbacks or initialize scan
* tasks. * tasks.
*/ */
@MainThread
protected abstract void onSlicePinned(); protected abstract void onSlicePinned();
/** /**
* Called when the Slice is unpinned. This is the place to unregister callbacks or perform any * Called when the Slice is unpinned. This is the place to unregister callbacks or perform any
* final cleanup. * final cleanup.
*/ */
@MainThread
protected abstract void onSliceUnpinned(); protected abstract void onSliceUnpinned();
/** /**

View File

@@ -30,8 +30,6 @@ import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid; import android.net.wifi.WifiSsid;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.SettingsSlicesContract; import android.provider.SettingsSlicesContract;
import android.text.TextUtils; import android.text.TextUtils;
@@ -62,7 +60,6 @@ import java.util.List;
*/ */
public class WifiSlice implements CustomSliceable { public class WifiSlice implements CustomSliceable {
/** /**
* Backing Uri for the Wifi Slice. * Backing Uri for the Wifi Slice.
*/ */
@@ -124,16 +121,16 @@ public class WifiSlice implements CustomSliceable {
return listBuilder.build(); return listBuilder.build();
} }
List<AccessPoint> result = getBackgroundWorker().getResults(); List<AccessPoint> results = getBackgroundWorker().getResults();
if (result == null) { if (results == null) {
result = new ArrayList<>(); results = new ArrayList<>();
} }
final int apCount = result.size(); final int apCount = results.size();
// Add AP rows // Add AP rows
final CharSequence placeholder = mContext.getText(R.string.summary_placeholder); final CharSequence placeholder = mContext.getText(R.string.summary_placeholder);
for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) { for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) {
if (i < apCount) { if (i < apCount) {
listBuilder.addRow(getAccessPointRow(result.get(i))); listBuilder.addRow(getAccessPointRow(results.get(i)));
} else { } else {
listBuilder.addRow(new RowBuilder() listBuilder.addRow(new RowBuilder()
.setTitle(placeholder) .setTitle(placeholder)
@@ -277,12 +274,12 @@ public class WifiSlice implements CustomSliceable {
private static class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> private static class WifiScanWorker extends SliceBackgroundWorker<AccessPoint>
implements WifiTracker.WifiListener { implements WifiTracker.WifiListener {
// TODO: enforce all the SliceBackgroundWorkers being singletons at syntax level
private static WifiScanWorker mWifiScanWorker; private static WifiScanWorker mWifiScanWorker;
private final Context mContext; private final Context mContext;
private WifiTracker mWifiTracker; private WifiTracker mWifiTracker;
private WifiManager mWifiManager;
private WifiScanWorker(Context context, Uri uri) { private WifiScanWorker(Context context, Uri uri) {
super(context.getContentResolver(), uri); super(context.getContentResolver(), uri);
@@ -298,17 +295,20 @@ public class WifiSlice implements CustomSliceable {
@Override @Override
protected void onSlicePinned() { protected void onSlicePinned() {
new Handler(Looper.getMainLooper()).post(() -> { if (mWifiTracker == null) {
mWifiTracker = new WifiTracker(mContext, this, true, true); mWifiTracker = new WifiTracker(mContext, this, true, true);
mWifiManager = mWifiTracker.getManager(); }
mWifiTracker.onStart(); mWifiTracker.onStart();
onAccessPointsChanged(); onAccessPointsChanged();
});
} }
@Override @Override
protected void onSliceUnpinned() { protected void onSliceUnpinned() {
mWifiTracker.onStop(); mWifiTracker.onStop();
}
@Override
public void close() {
mWifiTracker.onDestroy(); mWifiTracker.onDestroy();
mWifiScanWorker = null; mWifiScanWorker = null;
} }
@@ -324,7 +324,7 @@ public class WifiSlice implements CustomSliceable {
@Override @Override
public void onAccessPointsChanged() { public void onAccessPointsChanged() {
// in case state has changed // in case state has changed
if (!mWifiManager.isWifiEnabled()) { if (!mWifiTracker.getManager().isWifiEnabled()) {
updateResults(null); updateResults(null);
return; return;
} }

View File

@@ -67,6 +67,7 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter; import org.robolectric.annotation.Resetter;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -485,11 +486,15 @@ public class SettingsSliceProviderTest {
final SliceBackgroundWorker worker = spy(new SliceBackgroundWorker( final SliceBackgroundWorker worker = spy(new SliceBackgroundWorker(
mContext.getContentResolver(), uri) { mContext.getContentResolver(), uri) {
@Override @Override
public void onSlicePinned() { protected void onSlicePinned() {
} }
@Override @Override
public void onSliceUnpinned() { protected void onSliceUnpinned() {
}
@Override
public void close() {
} }
}); });
final WifiSlice wifiSlice = spy(new WifiSlice(mContext)); final WifiSlice wifiSlice = spy(new WifiSlice(mContext));
@@ -519,6 +524,17 @@ public class SettingsSliceProviderTest {
verify(worker).onSliceUnpinned(); verify(worker).onSliceUnpinned();
} }
@Test
public void shutdown_backgroundWorker_closed() throws IOException {
final Uri uri = WifiSlice.WIFI_URI;
final SliceBackgroundWorker worker = initBackgroundWorker(uri);
mProvider.onSlicePinned(uri);
mProvider.shutdown();
verify(worker).close();
}
@Test @Test
public void grantWhitelistedPackagePermissions_noWhitelist_shouldNotGrant() { public void grantWhitelistedPackagePermissions_noWhitelist_shouldNotGrant() {
final List<Uri> uris = new ArrayList<>(); final List<Uri> uris = new ArrayList<>();