blob: 40603268fa261ce1b56278299cea20896f950b07 [file] [log] [blame]
Christopher Tate8662cab52012-02-23 14:59:36 -08001/*
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
17package android.os;
18
19import android.content.Context;
20import 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 */
29public class UpdateLock {
30 private static final boolean DEBUG = false;
31 private static final String TAG = "UpdateLock";
32
33 private static IUpdateLock sService;
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 */
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 */
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 */
93 public boolean isHeld() {
94 synchronized (mToken) {
95 return mHeld;
96 }
97 }
98
99 /**
100 * Acquire an update lock.
101 */
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
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 */
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
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
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}