• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2012 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package android.os;
18  
19  import android.content.Context;
20  import android.util.Log;
21  
22  /**
23   * Advisory wakelock-like mechanism by which processes that should not be interrupted for
24   * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
25   * or kiosk-like operation.
26   *
27   * @hide
28   */
29  public class UpdateLock {
30      private static final boolean DEBUG = false;
31      private static final String TAG = "UpdateLock";
32  
33      private static IUpdateLock sService;
checkService()34      private static void checkService() {
35          if (sService == null) {
36              sService = IUpdateLock.Stub.asInterface(
37                      ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
38          }
39      }
40  
41      IBinder mToken;
42      int mCount = 0;
43      boolean mRefCounted = true;
44      boolean mHeld = false;
45      final String mTag;
46  
47      /**
48       * Broadcast Intent action sent when the global update lock state changes,
49       * i.e. when the first locker acquires an update lock, or when the last
50       * locker releases theirs.  The broadcast is sticky but is sent only to
51       * registered receivers.
52       */
53      public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
54  
55      /**
56       * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
57       * whether now is an appropriate time to interrupt device activity with an
58       * update operation.  True means that updates are okay right now; false indicates
59       * that perhaps later would be a better time.
60       */
61      public static final String NOW_IS_CONVENIENT = "nowisconvenient";
62  
63      /**
64       * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
65       * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
66       * in the System.currentTimeMillis() time base, which may be non-monotonic especially
67       * around reboots.
68       */
69      public static final String TIMESTAMP = "timestamp";
70  
71      /**
72       * Construct an UpdateLock instance.
73       * @param tag An arbitrary string used to identify this lock instance in dump output.
74       */
UpdateLock(String tag)75      public UpdateLock(String tag) {
76          mTag = tag;
77          mToken = new Binder();
78      }
79  
80      /**
81       * Change the refcount behavior of this update lock.
82       */
setReferenceCounted(boolean isRefCounted)83      public void setReferenceCounted(boolean isRefCounted) {
84          if (DEBUG) {
85              Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
86          }
87          mRefCounted = isRefCounted;
88      }
89  
90      /**
91       * Is this lock currently held?
92       */
isHeld()93      public boolean isHeld() {
94          synchronized (mToken) {
95              return mHeld;
96          }
97      }
98  
99      /**
100       * Acquire an update lock.
101       */
acquire()102      public void acquire() {
103          if (DEBUG) {
104              Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
105          }
106          checkService();
107          synchronized (mToken) {
108              acquireLocked();
109          }
110      }
111  
acquireLocked()112      private void acquireLocked() {
113          if (!mRefCounted || mCount++ == 0) {
114              if (sService != null) {
115                  try {
116                      sService.acquireUpdateLock(mToken, mTag);
117                  } catch (RemoteException e) {
118                      Log.e(TAG, "Unable to contact service to acquire");
119                  }
120              }
121              mHeld = true;
122          }
123      }
124  
125      /**
126       * Release this update lock.
127       */
release()128      public void release() {
129          if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
130          checkService();
131          synchronized (mToken) {
132              releaseLocked();
133          }
134      }
135  
releaseLocked()136      private void releaseLocked() {
137          if (!mRefCounted || --mCount == 0) {
138              if (sService != null) {
139                  try {
140                      sService.releaseUpdateLock(mToken);
141                  } catch (RemoteException e) {
142                      Log.e(TAG, "Unable to contact service to release");
143                  }
144              }
145              mHeld = false;
146          }
147          if (mCount < 0) {
148              throw new RuntimeException("UpdateLock under-locked");
149          }
150      }
151  
152      @Override
finalize()153      protected void finalize() throws Throwable {
154          synchronized (mToken) {
155              // if mHeld is true, sService must be non-null
156              if (mHeld) {
157                  Log.wtf(TAG, "UpdateLock finalized while still held");
158                  try {
159                      sService.releaseUpdateLock(mToken);
160                  } catch (RemoteException e) {
161                      Log.e(TAG, "Unable to contact service to release");
162                  }
163              }
164          }
165      }
166  }
167