content: /*
   * Copyright (C) 2012 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.server;
  
  import android.content.Context;
  import android.content.Intent;
  import android.content.pm.PackageManager;
  import android.os.Binder;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.IUpdateLock;
  import android.os.RemoteException;
  import android.os.TokenWatcher;
  import android.os.UpdateLock;
  import android.os.UserHandle;
  import android.util.Slog;
  
  import com.android.internal.util.DumpUtils;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  
  public class UpdateLockService extends IUpdateLock.Stub {
      static final boolean DEBUG = false;
      static final String TAG = "UpdateLockService";
  
      // signatureOrSystem required to use update locks
      static final String PERMISSION = "android.permission.UPDATE_LOCK";
  
      Context mContext;
      LockWatcher mLocks;
  
      class LockWatcher extends TokenWatcher {
          LockWatcher(Handler h, String tag) {
              super(h, tag);
          }
  
          public void acquired() {
              if (DEBUG) {
                  Slog.d(TAG, "first acquire; broadcasting convenient=false");
              }
              sendLockChangedBroadcast(false);
          }
          public void released() {
              if (DEBUG) {
                  Slog.d(TAG, "last release; broadcasting convenient=true");
              }
              sendLockChangedBroadcast(true);
          }
      }
  
      UpdateLockService(Context context) {
          mContext = context;
          mLocks = new LockWatcher(new Handler(), "UpdateLocks");
  
          // Consider just-booting to be a reasonable time to allow
          // interruptions for update installation etc.
          sendLockChangedBroadcast(true);
      }
  
      void sendLockChangedBroadcast(boolean state) {
          // Safe early during boot because this broadcast only goes to registered receivers.
          long oldIdent = Binder.clearCallingIdentity();
          try {
              Intent intent = new Intent(UpdateLock.UPDATE_LOCK_CHANGED)
                      .putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
                      .putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
                      .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
              mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
          } finally {
              Binder.restoreCallingIdentity(oldIdent);
          }
      }
  
      @Override
      public void acquireUpdateLock(IBinder token, String tag) throws RemoteException {
          if (DEBUG) {
              Slog.d(TAG, "acquire(" + token + ") by " + makeTag(tag));
          }
  
          mContext.enforceCallingOrSelfPermission(PERMISSION, "acquireUpdateLock");
          mLocks.acquire(token, makeTag(tag));
      }
  
      @Override
      public void releaseUpdateLock(IBinder token) throws RemoteException {
          if (DEBUG) {
              Slog.d(TAG, "release(" + token + ')');
          }
  
          mContext.enforceCallingOrSelfPermission(PERMISSION, "releaseUpdateLock");
          mLocks.release(token);
      };
  
      private String makeTag(String tag) {
          return "{tag=" + tag
                  + " uid=" + Binder.getCallingUid()
                  + " pid=" + Binder.getCallingPid() + '}';
      }
  
      @Override
      public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
          mLocks.dump(pw);
      }
  }
  