Creates a BootCompleteCache
This cache is set by SystemUIApplication on boot and can be queried or subscribed to. The cache registration turns into no-op after boot, returning the boot state instead. LocationControllerImpl uses this cache to determine if it can query LocationManager Test: atest BootCompleteCacheTest Fixes: 145019350 Change-Id: I2e34e53bfb8beca730cec8da6125988554c659e4
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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
|
||||
|
||||
interface BootCompleteCache {
|
||||
fun isBootComplete(): Boolean
|
||||
fun addListener(listener: BootCompleteListener): Boolean
|
||||
fun removeListener(listener: BootCompleteListener)
|
||||
|
||||
interface BootCompleteListener {
|
||||
fun onBootComplete()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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
|
||||
|
||||
import android.util.Log
|
||||
import com.android.internal.annotations.GuardedBy
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Caches whether the device has reached [SystemService.PHASE_BOOT_COMPLETED].
|
||||
*
|
||||
* This class is constructed and set by [SystemUIApplication] and will notify all listeners when
|
||||
* boot is completed.
|
||||
*/
|
||||
@Singleton
|
||||
class BootCompleteCacheImpl @Inject constructor(private val dumpController: DumpController) :
|
||||
BootCompleteCache, Dumpable {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BootCompleteCacheImpl"
|
||||
private const val DEBUG = false
|
||||
}
|
||||
|
||||
init {
|
||||
dumpController.registerDumpable(TAG, this)
|
||||
}
|
||||
|
||||
@GuardedBy("listeners")
|
||||
private val listeners = mutableListOf<WeakReference<BootCompleteCache.BootCompleteListener>>()
|
||||
private val bootComplete = AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
* Provides the current boot state of the system as determined by [SystemUIApplication].
|
||||
* @return `true` if the system has reached [SystemService.PHASE_BOOT_COMPLETED]
|
||||
*/
|
||||
override fun isBootComplete(): Boolean = bootComplete.get()
|
||||
|
||||
/**
|
||||
* Indicates to this object that boot is complete. Subsequent calls to this function will have
|
||||
* no effect.
|
||||
*/
|
||||
fun setBootComplete() {
|
||||
if (bootComplete.compareAndSet(false, true)) {
|
||||
if (DEBUG) Log.d(TAG, "Boot complete set")
|
||||
synchronized(listeners) {
|
||||
listeners.forEach {
|
||||
it.get()?.onBootComplete()
|
||||
}
|
||||
listeners.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for boot complete event. It will immediately return the current boot complete
|
||||
* state. If this value is true, [BootCompleteCache.BootCompleteListener.onBootComplete] will
|
||||
* never be called.
|
||||
*
|
||||
* @param listener a listener for boot complete state.
|
||||
* @return `true` if boot has been completed.
|
||||
*/
|
||||
override fun addListener(listener: BootCompleteCache.BootCompleteListener): Boolean {
|
||||
if (bootComplete.get()) return true
|
||||
synchronized(listeners) {
|
||||
if (bootComplete.get()) return true
|
||||
listeners.add(WeakReference(listener))
|
||||
if (DEBUG) Log.d(TAG, "Adding listener: $listener")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for boot complete event.
|
||||
*
|
||||
* @param listener a listener to removed.
|
||||
*/
|
||||
override fun removeListener(listener: BootCompleteCache.BootCompleteListener) {
|
||||
if (bootComplete.get()) return
|
||||
synchronized(listeners) {
|
||||
listeners.removeIf { it.get() == null || it.get() === listener }
|
||||
if (DEBUG) Log.d(TAG, "Removing listener: $listener")
|
||||
}
|
||||
}
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
|
||||
pw.println("BootCompleteCache state:")
|
||||
pw.println(" boot complete: ${isBootComplete()}")
|
||||
if (!isBootComplete()) {
|
||||
pw.println(" listeners:")
|
||||
synchronized(listeners) {
|
||||
listeners.forEach {
|
||||
pw.println(" $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import android.util.Log;
|
||||
import android.util.TimingsTraceLog;
|
||||
|
||||
import com.android.systemui.dagger.ContextComponentHelper;
|
||||
import com.android.systemui.dagger.SystemUIRootComponent;
|
||||
import com.android.systemui.util.NotificationChannels;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -47,13 +48,13 @@ public class SystemUIApplication extends Application implements
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private ContextComponentHelper mComponentHelper;
|
||||
private BootCompleteCacheImpl mBootCompleteCache;
|
||||
|
||||
/**
|
||||
* Hold a reference on the stuff we start.
|
||||
*/
|
||||
private SystemUI[] mServices;
|
||||
private boolean mServicesStarted;
|
||||
private boolean mBootCompleted;
|
||||
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
|
||||
|
||||
public SystemUIApplication() {
|
||||
@@ -71,8 +72,9 @@ public class SystemUIApplication extends Application implements
|
||||
Trace.TRACE_TAG_APP);
|
||||
log.traceBegin("DependencyInjection");
|
||||
mContextAvailableCallback.onContextAvailable(this);
|
||||
mComponentHelper = SystemUIFactory
|
||||
.getInstance().getRootComponent().getContextComponentHelper();
|
||||
SystemUIRootComponent root = SystemUIFactory.getInstance().getRootComponent();
|
||||
mComponentHelper = root.getContextComponentHelper();
|
||||
mBootCompleteCache = root.provideBootCacheImpl();
|
||||
log.traceEnd();
|
||||
|
||||
// Set the application theme that is inherited by all services. Note that setting the
|
||||
@@ -86,19 +88,17 @@ public class SystemUIApplication extends Application implements
|
||||
registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (mBootCompleted) return;
|
||||
if (mBootCompleteCache.isBootComplete()) return;
|
||||
|
||||
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
|
||||
unregisterReceiver(this);
|
||||
mBootCompleted = true;
|
||||
mBootCompleteCache.setBootComplete();
|
||||
if (mServicesStarted) {
|
||||
final int N = mServices.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
mServices[i].onBootCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}, bootCompletedFilter);
|
||||
|
||||
@@ -107,7 +107,7 @@ public class SystemUIApplication extends Application implements
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
|
||||
if (!mBootCompleted) return;
|
||||
if (!mBootCompleteCache.isBootComplete()) return;
|
||||
// Update names of SystemUi notification channels
|
||||
NotificationChannels.createAll(context);
|
||||
}
|
||||
@@ -159,11 +159,11 @@ public class SystemUIApplication extends Application implements
|
||||
}
|
||||
mServices = new SystemUI[services.length];
|
||||
|
||||
if (!mBootCompleted) {
|
||||
if (!mBootCompleteCache.isBootComplete()) {
|
||||
// check to see if maybe it was already completed long before we began
|
||||
// see ActivityManagerService.finishBooting()
|
||||
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
|
||||
mBootCompleted = true;
|
||||
mBootCompleteCache.setBootComplete();
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "BOOT_COMPLETED was already sent");
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public class SystemUIApplication extends Application implements
|
||||
if (ti > 1000) {
|
||||
Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
|
||||
}
|
||||
if (mBootCompleted) {
|
||||
if (mBootCompleteCache.isBootComplete()) {
|
||||
mServices[i].onBootCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class SystemUIFactory {
|
||||
private static final String TAG = "SystemUIFactory";
|
||||
|
||||
static SystemUIFactory mFactory;
|
||||
protected SystemUIRootComponent mRootComponent;
|
||||
private SystemUIRootComponent mRootComponent;
|
||||
|
||||
public static <T extends SystemUIFactory> T getInstance() {
|
||||
return (T) mFactory;
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.BootCompleteCache;
|
||||
import com.android.systemui.BootCompleteCacheImpl;
|
||||
import com.android.systemui.DumpController;
|
||||
import com.android.systemui.assist.AssistModule;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
@@ -52,6 +54,10 @@ import dagger.Provides;
|
||||
@Module(includes = {AssistModule.class,
|
||||
PeopleHubModule.class})
|
||||
public abstract class SystemUIModule {
|
||||
|
||||
@Binds
|
||||
abstract BootCompleteCache bindBootCompleteCache(BootCompleteCacheImpl bootCompleteCache);
|
||||
|
||||
/** */
|
||||
@Binds
|
||||
public abstract ContextComponentHelper bindComponentHelper(
|
||||
@@ -100,4 +106,5 @@ public abstract class SystemUIModule {
|
||||
@Singleton
|
||||
@Binds
|
||||
abstract NotifListBuilder bindNotifListBuilder(NotifListBuilderImpl impl);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME
|
||||
|
||||
import android.content.ContentProvider;
|
||||
|
||||
import com.android.systemui.BootCompleteCacheImpl;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SystemUIAppComponentFactory;
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
@@ -47,6 +48,12 @@ import dagger.Component;
|
||||
SystemUIDefaultModule.class})
|
||||
public interface SystemUIRootComponent {
|
||||
|
||||
/**
|
||||
* Provides a BootCompleteCache.
|
||||
*/
|
||||
@Singleton
|
||||
BootCompleteCacheImpl provideBootCacheImpl();
|
||||
|
||||
/**
|
||||
* Creates a ContextComponentHelper.
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.BootCompleteCache;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.qualifiers.BgLooper;
|
||||
import com.android.systemui.util.Utils;
|
||||
@@ -59,6 +60,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private StatusBarManager mStatusBarManager;
|
||||
private BroadcastDispatcher mBroadcastDispatcher;
|
||||
private BootCompleteCache mBootCompleteCache;
|
||||
|
||||
private boolean mAreActiveLocationRequests;
|
||||
|
||||
@@ -68,9 +70,10 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
|
||||
|
||||
@Inject
|
||||
public LocationControllerImpl(Context context, @BgLooper Looper bgLooper,
|
||||
BroadcastDispatcher broadcastDispatcher) {
|
||||
BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) {
|
||||
mContext = context;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
mBootCompleteCache = bootCompleteCache;
|
||||
|
||||
// Register to listen for changes in location settings.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -124,14 +127,15 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if location is enabled in settings.
|
||||
* Returns true if location is enabled in settings. Will return false if
|
||||
* {@link LocationManager} service has not been completely initialized
|
||||
*/
|
||||
public boolean isLocationEnabled() {
|
||||
// QuickSettings always runs as the owner, so specifically retrieve the settings
|
||||
// for the current foreground user.
|
||||
LocationManager locationManager =
|
||||
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
||||
return locationManager.isLocationEnabledForUser(
|
||||
return mBootCompleteCache.isBootComplete() && locationManager.isLocationEnabledForUser(
|
||||
UserHandle.of(ActivityManager.getCurrentUser()));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class BootCompleteCacheTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var bootCompleteCache: BootCompleteCacheImpl
|
||||
@Mock
|
||||
private lateinit var bootCompleteListener: BootCompleteCache.BootCompleteListener
|
||||
@Mock
|
||||
private lateinit var dumpController: DumpController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
bootCompleteCache = BootCompleteCacheImpl(dumpController)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFlagChange() {
|
||||
assertFalse(bootCompleteCache.isBootComplete())
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
assertTrue(bootCompleteCache.isBootComplete())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoubleSetIsNoOp() {
|
||||
assertFalse(bootCompleteCache.isBootComplete())
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
assertTrue(bootCompleteCache.isBootComplete())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddListenerGivesCurrentState_false() {
|
||||
val boot = bootCompleteCache.addListener(bootCompleteListener)
|
||||
assertFalse(boot)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddListenerGivesCurrentState_true() {
|
||||
bootCompleteCache.setBootComplete()
|
||||
val boot = bootCompleteCache.addListener(bootCompleteListener)
|
||||
assertTrue(boot)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListenerCalledOnBootComplete() {
|
||||
bootCompleteCache.addListener(bootCompleteListener)
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
verify(bootCompleteListener).onBootComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListenerCalledOnBootComplete_onlyOnce() {
|
||||
bootCompleteCache.addListener(bootCompleteListener)
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
verify(bootCompleteListener).onBootComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListenerNotCalledIfBootIsAlreadyComplete() {
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
bootCompleteCache.addListener(bootCompleteListener)
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
verify(bootCompleteListener, never()).onBootComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListenerRemovedNotCalled() {
|
||||
bootCompleteCache.addListener(bootCompleteListener)
|
||||
|
||||
bootCompleteCache.removeListener(bootCompleteListener)
|
||||
|
||||
bootCompleteCache.setBootComplete()
|
||||
|
||||
verify(bootCompleteListener, never()).onBootComplete()
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.BootCompleteCache;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
|
||||
@@ -46,7 +47,8 @@ public class LocationControllerImplTest extends SysuiTestCase {
|
||||
public void setup() {
|
||||
mLocationController = spy(new LocationControllerImpl(mContext,
|
||||
TestableLooper.get(this).getLooper(),
|
||||
mock(BroadcastDispatcher.class)));
|
||||
mock(BroadcastDispatcher.class),
|
||||
mock(BootCompleteCache.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user