diff options
| -rw-r--r-- | core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java | 87 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 6 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 3 | ||||
| -rw-r--r-- | include/androidfw/Asset.h | 15 | ||||
| -rw-r--r-- | libs/androidfw/Asset.cpp | 92 | ||||
| -rw-r--r-- | libs/androidfw/tests/Android.mk | 1 | ||||
| -rw-r--r-- | libs/androidfw/tests/Asset_test.cpp | 37 | ||||
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java | 11 | ||||
| -rw-r--r-- | packages/SystemUI/res/values/config.xml | 6 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/doze/DozeService.java | 14 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java | 19 | ||||
| -rw-r--r-- | services/core/java/com/android/server/dreams/DreamManagerService.java | 25 | 
12 files changed, 235 insertions, 81 deletions
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java new file mode 100644 index 000000000000..cf1bf62cdeb2 --- /dev/null +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 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.internal.hardware; + +import com.android.internal.R; + +import android.content.Context; +import android.provider.Settings; +import android.text.TextUtils; + +public class AmbientDisplayConfiguration { + +    private final Context mContext; + +    public AmbientDisplayConfiguration(Context context) { +        mContext = context; +    } +     +    public boolean enabled(int user) { +        return pulseOnNotificationEnabled(user) +                || pulseOnPickupEnabled(user) +                || pulseOnDoubleTapEnabled(user); +    } +     +    public boolean available() { +        return pulseOnNotificationAvailable() || pulseOnPickupAvailable() +                || pulseOnDoubleTapAvailable(); +    } +     +    public boolean pulseOnNotificationEnabled(int user) { +        return boolSetting(Settings.Secure.DOZE_ENABLED, user) && pulseOnNotificationAvailable(); +    } + +    public boolean pulseOnNotificationAvailable() { +        return ambientDisplayAvailable(); +    } + +    public boolean pulseOnPickupEnabled(int user) { +        return boolSetting(Settings.Secure.DOZE_PULSE_ON_PICK_UP, user) +                && pulseOnPickupAvailable(); +    } +     +    public boolean pulseOnPickupAvailable() { +        return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup) +                && ambientDisplayAvailable(); +    } +     +    public boolean pulseOnDoubleTapEnabled(int user) { +        return boolSetting(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, user) +                && pulseOnDoubleTapAvailable(); +    } + +    public boolean pulseOnDoubleTapAvailable() { +        return !TextUtils.isEmpty(doubleTapSensorType()) && ambientDisplayAvailable(); +    } + +    public String doubleTapSensorType() { +        return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType); +    } + +    public String ambientDisplayComponent() { +        return mContext.getResources().getString(R.string.config_dozeComponent); +    } + +    private boolean ambientDisplayAvailable() { +        return !TextUtils.isEmpty(ambientDisplayComponent()); +    } + +    private boolean boolSetting(String name, int user) { +        return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, 1, user) != 0; +    } + +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 18467ae3a11e..ae8edabae8c5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1661,6 +1661,12 @@           turned off and the screen off animation has been performed. -->      <bool name="config_dozeAfterScreenOff">false</bool> +    <!-- Doze: should the TYPE_PICK_UP_GESTURE sensor be used as a pulse signal. --> +    <bool name="config_dozePulsePickup">false</bool> + +    <!-- Type of the double tap sensor. Empty if double tap is not supported. --> +    <string name="config_dozeDoubleTapSensorType" translatable="false"></string> +      <!-- Power Management: Specifies whether to decouple the auto-suspend state of the           device from the display on/off state. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b03a61663f98..826161e3228c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2668,6 +2668,9 @@    <java-symbol type="string" name="config_emergency_call_number" />    <java-symbol type="array" name="config_emergency_mcc_codes" /> +  <java-symbol type="string" name="config_dozeDoubleTapSensorType" /> +  <java-symbol type="bool" name="config_dozePulsePickup" /> +    <!-- Used for MimeIconUtils. -->    <java-symbol type="drawable" name="ic_doc_apk" />    <java-symbol type="drawable" name="ic_doc_audio" /> diff --git a/include/androidfw/Asset.h b/include/androidfw/Asset.h index ee77e971011b..52c863774efb 100644 --- a/include/androidfw/Asset.h +++ b/include/androidfw/Asset.h @@ -44,7 +44,7 @@ namespace android {   */  class Asset {  public: -    virtual ~Asset(void); +    virtual ~Asset(void) = default;      static int32_t getGlobalCount();      static String8 getAssetAllocations(); @@ -119,6 +119,19 @@ public:      const char* getAssetSource(void) const { return mAssetSource.string(); }  protected: +    /* +     * Adds this Asset to the global Asset list for debugging and +     * accounting. +     * Concrete subclasses must call this in their constructor. +     */ +    static void registerAsset(Asset* asset); + +    /* +     * Removes this Asset from the global Asset list. +     * Concrete subclasses must call this in their destructor. +     */ +    static void unregisterAsset(Asset* asset); +      Asset(void);        // constructor; only invoked indirectly      /* handle common seek() housekeeping */ diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 4e14b13dc8bd..6ae39d5d5d01 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -52,6 +52,47 @@ static int32_t gCount = 0;  static Asset* gHead = NULL;  static Asset* gTail = NULL; +void Asset::registerAsset(Asset* asset) +{ +    AutoMutex _l(gAssetLock); +    gCount++; +    asset->mNext = asset->mPrev = NULL; +    if (gTail == NULL) { +        gHead = gTail = asset; +    } else { +        asset->mPrev = gTail; +        gTail->mNext = asset; +        gTail = asset; +    } + +    if (kIsDebug) { +        ALOGI("Creating Asset %p #%d\n", asset, gCount); +    } +} + +void Asset::unregisterAsset(Asset* asset) +{ +    AutoMutex _l(gAssetLock); +    gCount--; +    if (gHead == asset) { +        gHead = asset->mNext; +    } +    if (gTail == asset) { +        gTail = asset->mPrev; +    } +    if (asset->mNext != NULL) { +        asset->mNext->mPrev = asset->mPrev; +    } +    if (asset->mPrev != NULL) { +        asset->mPrev->mNext = asset->mNext; +    } +    asset->mNext = asset->mPrev = NULL; + +    if (kIsDebug) { +        ALOGI("Destroying Asset in %p #%d\n", asset, gCount); +    } +} +  int32_t Asset::getGlobalCount()  {      AutoMutex _l(gAssetLock); @@ -79,43 +120,8 @@ String8 Asset::getAssetAllocations()  }  Asset::Asset(void) -    : mAccessMode(ACCESS_UNKNOWN) +    : mAccessMode(ACCESS_UNKNOWN), mNext(NULL), mPrev(NULL)  { -    AutoMutex _l(gAssetLock); -    gCount++; -    mNext = mPrev = NULL; -    if (gTail == NULL) { -        gHead = gTail = this; -    } else { -        mPrev = gTail; -        gTail->mNext = this; -        gTail = this; -    } -    if (kIsDebug) { -        ALOGI("Creating Asset %p #%d\n", this, gCount); -    } -} - -Asset::~Asset(void) -{ -    AutoMutex _l(gAssetLock); -    gCount--; -    if (gHead == this) { -        gHead = mNext; -    } -    if (gTail == this) { -        gTail = mPrev; -    } -    if (mNext != NULL) { -        mNext->mPrev = mPrev; -    } -    if (mPrev != NULL) { -        mPrev->mNext = mNext; -    } -    mNext = mPrev = NULL; -    if (kIsDebug) { -        ALOGI("Destroying Asset in %p #%d\n", this, gCount); -    }  }  /* @@ -361,6 +367,9 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m  _FileAsset::_FileAsset(void)      : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)  { +    // Register the Asset with the global list here after it is fully constructed and its +    // vtable pointer points to this concrete type. b/31113965 +    registerAsset(this);  }  /* @@ -369,6 +378,10 @@ _FileAsset::_FileAsset(void)  _FileAsset::~_FileAsset(void)  {      close(); + +    // Unregister the Asset from the global list here before it is destructed and while its vtable +    // pointer still points to this concrete type. b/31113965 +    unregisterAsset(this);  }  /* @@ -685,6 +698,9 @@ _CompressedAsset::_CompressedAsset(void)      : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),        mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)  { +    // Register the Asset with the global list here after it is fully constructed and its +    // vtable pointer points to this concrete type. b/31113965 +    registerAsset(this);  }  /* @@ -693,6 +709,10 @@ _CompressedAsset::_CompressedAsset(void)  _CompressedAsset::~_CompressedAsset(void)  {      close(); + +    // Unregister the Asset from the global list here before it is destructed and while its vtable +    // pointer still points to this concrete type. b/31113965 +    unregisterAsset(this);  }  /* diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 2bc026b79ca2..1fe1773578fa 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -22,6 +22,7 @@ LOCAL_PATH:= $(call my-dir)  testFiles := \      AppAsLib_test.cpp \ +    Asset_test.cpp \      AttributeFinder_test.cpp \      ByteBucketArray_test.cpp \      Config_test.cpp \ diff --git a/libs/androidfw/tests/Asset_test.cpp b/libs/androidfw/tests/Asset_test.cpp new file mode 100644 index 000000000000..45c8cef92918 --- /dev/null +++ b/libs/androidfw/tests/Asset_test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 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. + */ + +#include <androidfw/Asset.h> + +#include <gtest/gtest.h> + +using namespace android; + +TEST(AssetTest, FileAssetRegistersItself) { +    const int32_t count = Asset::getGlobalCount(); +    Asset* asset = new _FileAsset(); +    EXPECT_EQ(count + 1, Asset::getGlobalCount()); +    delete asset; +    EXPECT_EQ(count, Asset::getGlobalCount()); +} + +TEST(AssetTest, CompressedAssetRegistersItself) { +    const int32_t count = Asset::getGlobalCount(); +    Asset* asset = new _CompressedAsset(); +    EXPECT_EQ(count + 1, Asset::getGlobalCount()); +    delete asset; +    EXPECT_EQ(count, Asset::getGlobalCount()); +} diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index b63752c22521..234ae712c49e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -356,7 +356,11 @@ public class AccessPoint implements Comparable<AccessPoint> {      }      public DetailedState getDetailedState() { -        return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null; +        if (mNetworkInfo != null) { +            return mNetworkInfo.getDetailedState(); +        } +        Log.w(TAG, "NetworkInfo is null, cannot return detailed state"); +        return null;      }      public String getSavedNetworkSummary() { @@ -798,7 +802,10 @@ public class AccessPoint implements Comparable<AccessPoint> {                  return context.getString(R.string.wifi_connected_no_internet);              }          } - +        if (state == null) { +            Log.w(TAG, "state is null, returning empty summary"); +            return ""; +        }          String[] formats = context.getResources().getStringArray((ssid == null)                  ? R.array.wifi_status : R.array.wifi_status_with_ssid);          int index = state.ordinal(); diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index eb1a1ebc0850..9eea3750a8ec 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -209,9 +209,6 @@      <!-- Doze: should the significant motion sensor be used as a pulse signal? -->      <bool name="doze_pulse_on_significant_motion">false</bool> -    <!-- Doze: should the pickup sensor be used as a pulse signal? --> -    <bool name="doze_pulse_on_pick_up">false</bool> -      <!-- Doze: check proximity sensor before pulsing? -->      <bool name="doze_proximity_check_before_pulse">true</bool> @@ -240,9 +237,6 @@      -->      <string name="doze_pickup_subtype_performs_proximity_check"></string> -    <!-- Type of the double tap sensor. Empty if double tap is not supported. --> -    <string name="doze_double_tap_sensor_type" translatable="false"></string> -      <!-- Doze: pulse parameter - how long does it take to fade in? -->      <integer name="doze_pulse_duration_in">900</integer> diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 56f6b8df866b..6c3524356818 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -43,6 +43,7 @@ import android.text.TextUtils;  import android.util.Log;  import android.view.Display; +import com.android.internal.hardware.AmbientDisplayConfiguration;  import com.android.internal.logging.MetricsLogger;  import com.android.internal.logging.MetricsProto.MetricsEvent;  import com.android.systemui.SystemUIApplication; @@ -85,6 +86,8 @@ public class DozeService extends DreamService {      private boolean mCarMode;      private long mNotificationPulseTime; +    private AmbientDisplayConfiguration mConfig; +      public DozeService() {          if (DEBUG) Log.d(mTag, "new DozeService()");          setDebug(DEBUG); @@ -125,6 +128,7 @@ public class DozeService extends DreamService {          setWindowless(true);          mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); +        mConfig = new AmbientDisplayConfiguration(mContext);          mSensors = new TriggerSensor[] {                  new TriggerSensor(                          mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), @@ -135,12 +139,12 @@ public class DozeService extends DreamService {                  mPickupSensor = new TriggerSensor(                          mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),                          Settings.Secure.DOZE_PULSE_ON_PICK_UP, -                        mDozeParameters.getPulseOnPickup(), mDozeParameters.getVibrateOnPickup(), +                        mConfig.pulseOnPickupAvailable(), mDozeParameters.getVibrateOnPickup(),                          DozeLog.PULSE_REASON_SENSOR_PICKUP),                  new TriggerSensor( -                        findSensorWithType(mDozeParameters.getDoubleTapSensorType()), -                        Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, -                        mDozeParameters.getPulseOnPickup(), mDozeParameters.getVibrateOnPickup(), +                        findSensorWithType(mConfig.doubleTapSensorType()), +                        Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, true, +                        mDozeParameters.getVibrateOnPickup(),                          DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)          };          mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -359,7 +363,7 @@ public class DozeService extends DreamService {      private void requestNotificationPulse() {          if (DEBUG) Log.d(mTag, "requestNotificationPulse"); -        if (!mDozeParameters.getPulseOnNotifications()) return; +        if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return;          mNotificationPulseTime = SystemClock.elapsedRealtime();          requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index d5bf49991cbb..9e9bdd7a172e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -18,11 +18,13 @@ package com.android.systemui.statusbar.phone;  import android.content.Context;  import android.os.SystemProperties; +import android.os.UserHandle;  import android.text.TextUtils;  import android.util.Log;  import android.util.MathUtils;  import android.util.SparseBooleanArray; +import com.android.internal.hardware.AmbientDisplayConfiguration;  import com.android.systemui.R;  import java.io.PrintWriter; @@ -31,9 +33,6 @@ import java.util.regex.Matcher;  import java.util.regex.Pattern;  public class DozeParameters { -    private static final String TAG = "DozeParameters"; -    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); -      private static final int MAX_DURATION = 60 * 1000;      private final Context mContext; @@ -55,10 +54,8 @@ public class DozeParameters {          pw.print("    getPulseOutDuration(): "); pw.println(getPulseOutDuration());          pw.print("    getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());          pw.print("    getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion()); -        pw.print("    getPulseOnPickup(): "); pw.println(getPulseOnPickup());          pw.print("    getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());          pw.print("    getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); -        pw.print("    getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());          pw.print("    getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());          pw.print("    getPickupSubtypePerformsProxCheck(): ");pw.println(                  dumpPickupSubtypePerformsProxCheck()); @@ -106,26 +103,14 @@ public class DozeParameters {          return SystemProperties.getBoolean("doze.vibrate.sigmotion", false);      } -    public boolean getPulseOnPickup() { -        return getBoolean("doze.pulse.pickup", R.bool.doze_pulse_on_pick_up); -    } -      public boolean getVibrateOnPickup() {          return SystemProperties.getBoolean("doze.vibrate.pickup", false);      } -    public String getDoubleTapSensorType() { -        return mContext.getString(R.string.doze_double_tap_sensor_type); -    } -      public boolean getProxCheckBeforePulse() {          return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);      } -    public boolean getPulseOnNotifications() { -        return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications); -    } -      public int getPickupVibrationThreshold() {          return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);      } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 20bccf19f131..1991c00ff4e1 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -18,6 +18,7 @@ package com.android.server.dreams;  import static android.Manifest.permission.BIND_DREAM_SERVICE; +import com.android.internal.hardware.AmbientDisplayConfiguration;  import com.android.internal.util.DumpUtils;  import com.android.server.FgThread;  import com.android.server.LocalServices; @@ -88,6 +89,8 @@ public final class DreamManagerService extends SystemService {      private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;      private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; +    private AmbientDisplayConfiguration mDozeConfig; +      public DreamManagerService(Context context) {          super(context);          mContext = context; @@ -97,6 +100,7 @@ public final class DreamManagerService extends SystemService {          mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);          mPowerManagerInternal = getLocalService(PowerManagerInternal.class);          mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG); +        mDozeConfig = new AmbientDisplayConfiguration(mContext);      }      @Override @@ -121,7 +125,7 @@ public final class DreamManagerService extends SystemService {                  }              }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);              mContext.getContentResolver().registerContentObserver( -                    Settings.Secure.getUriFor(Settings.Secure.DOZE_ENABLED), false, +                    Settings.Secure.getUriFor(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP), false,                      mDozeEnabledObserver, UserHandle.USER_ALL);              writePulseGestureEnabled();          } @@ -326,19 +330,12 @@ public final class DreamManagerService extends SystemService {      }      private ComponentName getDozeComponent(int userId) { -        // Read the component from a system property to facilitate debugging. -        // Note that for production devices, the dream should actually be declared in -        // a config.xml resource. -        String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null; -        if (TextUtils.isEmpty(name)) { -            // Read the component from a config.xml resource. -            // The value should be specified in a resource overlay for the product. -            name = mContext.getResources().getString( -                    com.android.internal.R.string.config_dozeComponent); -        } -        boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), -                Settings.Secure.DOZE_ENABLED, 1, userId) != 0; -        return TextUtils.isEmpty(name) || !enabled ? null : ComponentName.unflattenFromString(name); +        if (mDozeConfig.enabled(userId)) { +            return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent()); +        } else { +            return null; +        } +      }      private ServiceInfo getServiceInfo(ComponentName name) {  |