1. Now a user state has ins own spooler since the spooler app is running per user. The user state registers an observer for the state of the spooler to get information needed to orchestrate unbinding from print serivces that have no work and eventually unbinding from the spooler when all no service has any work. 2. Abstracted a remote print service from the perspective of the system in a class that is transparently managing binding and unbinding to the remote instance. 3. Abstracted the remote print spooler to transparently manage binding and unbinding to the remote instance when there is work and when there is no work, respectively. 4. Cleaned up the print document adapter (ex-PrintAdapter) APIs to enable implementing the all callbacks on a thread of choice. If the document is really small, using the main thread makes sense. Now if an app that does not need the UI state to layout the printed content, it can schedule all the work for allocating resources, laying out, writing, and releasing resources on a dedicated thread. 5. Added info class for the printed document that is now propagated the the print services. A print service gets an instance of a new document class that encapsulates the document info and a method to access the document's data. 6. Added APIs for describing the type of a document to the new document info class. This allows a print service to do smarts based on the doc type. For now we have only photo and document types. 7. Renamed the systemReady method for system services that implement it with different semantics to systemRunning. Such methods assume the the service can run third-party code which is not the same as systemReady. 8. Cleaned up the print job configuration activity. 9. Sigh... code clean up here and there. Factoring out classes to improve readability. Change-Id: I637ba28412793166cbf519273fdf022241159a92
227 lines
6.8 KiB
Java
227 lines
6.8 KiB
Java
/*
|
|
* Copyright (C) 2010 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 java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.util.HashMap;
|
|
|
|
import com.android.internal.os.BackgroundThread;
|
|
import com.android.server.location.ComprehensiveCountryDetector;
|
|
|
|
import android.content.Context;
|
|
import android.location.Country;
|
|
import android.location.CountryListener;
|
|
import android.location.ICountryDetector;
|
|
import android.location.ICountryListener;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.os.RemoteException;
|
|
import android.util.PrintWriterPrinter;
|
|
import android.util.Printer;
|
|
import android.util.Slog;
|
|
|
|
/**
|
|
* This class detects the country that the user is in through
|
|
* {@link ComprehensiveCountryDetector}.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
|
|
|
|
/**
|
|
* The class represents the remote listener, it will also removes itself
|
|
* from listener list when the remote process was died.
|
|
*/
|
|
private final class Receiver implements IBinder.DeathRecipient {
|
|
private final ICountryListener mListener;
|
|
private final IBinder mKey;
|
|
|
|
public Receiver(ICountryListener listener) {
|
|
mListener = listener;
|
|
mKey = listener.asBinder();
|
|
}
|
|
|
|
public void binderDied() {
|
|
removeListener(mKey);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object otherObj) {
|
|
if (otherObj instanceof Receiver) {
|
|
return mKey.equals(((Receiver) otherObj).mKey);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return mKey.hashCode();
|
|
}
|
|
|
|
public ICountryListener getListener() {
|
|
return mListener;
|
|
}
|
|
}
|
|
|
|
private final static String TAG = "CountryDetector";
|
|
|
|
/** Whether to dump the state of the country detector service to bugreports */
|
|
private static final boolean DEBUG = false;
|
|
|
|
private final HashMap<IBinder, Receiver> mReceivers;
|
|
private final Context mContext;
|
|
private ComprehensiveCountryDetector mCountryDetector;
|
|
private boolean mSystemReady;
|
|
private Handler mHandler;
|
|
private CountryListener mLocationBasedDetectorListener;
|
|
|
|
public CountryDetectorService(Context context) {
|
|
super();
|
|
mReceivers = new HashMap<IBinder, Receiver>();
|
|
mContext = context;
|
|
}
|
|
|
|
@Override
|
|
public Country detectCountry() throws RemoteException {
|
|
if (!mSystemReady) {
|
|
throw new RemoteException();
|
|
}
|
|
return mCountryDetector.detectCountry();
|
|
}
|
|
|
|
/**
|
|
* Add the ICountryListener into the listener list.
|
|
*/
|
|
@Override
|
|
public void addCountryListener(ICountryListener listener) throws RemoteException {
|
|
if (!mSystemReady) {
|
|
throw new RemoteException();
|
|
}
|
|
addListener(listener);
|
|
}
|
|
|
|
/**
|
|
* Remove the ICountryListener from the listener list.
|
|
*/
|
|
@Override
|
|
public void removeCountryListener(ICountryListener listener) throws RemoteException {
|
|
if (!mSystemReady) {
|
|
throw new RemoteException();
|
|
}
|
|
removeListener(listener.asBinder());
|
|
}
|
|
|
|
private void addListener(ICountryListener listener) {
|
|
synchronized (mReceivers) {
|
|
Receiver r = new Receiver(listener);
|
|
try {
|
|
listener.asBinder().linkToDeath(r, 0);
|
|
mReceivers.put(listener.asBinder(), r);
|
|
if (mReceivers.size() == 1) {
|
|
Slog.d(TAG, "The first listener is added");
|
|
setCountryListener(mLocationBasedDetectorListener);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Slog.e(TAG, "linkToDeath failed:", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void removeListener(IBinder key) {
|
|
synchronized (mReceivers) {
|
|
mReceivers.remove(key);
|
|
if (mReceivers.isEmpty()) {
|
|
setCountryListener(null);
|
|
Slog.d(TAG, "No listener is left");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected void notifyReceivers(Country country) {
|
|
synchronized(mReceivers) {
|
|
for (Receiver receiver : mReceivers.values()) {
|
|
try {
|
|
receiver.getListener().onCountryDetected(country);
|
|
} catch (RemoteException e) {
|
|
// TODO: Shall we remove the receiver?
|
|
Slog.e(TAG, "notifyReceivers failed:", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void systemRunning() {
|
|
// Shall we wait for the initialization finish.
|
|
BackgroundThread.getHandler().post(this);
|
|
}
|
|
|
|
private void initialize() {
|
|
mCountryDetector = new ComprehensiveCountryDetector(mContext);
|
|
mLocationBasedDetectorListener = new CountryListener() {
|
|
public void onCountryDetected(final Country country) {
|
|
mHandler.post(new Runnable() {
|
|
public void run() {
|
|
notifyReceivers(country);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
public void run() {
|
|
mHandler = new Handler();
|
|
initialize();
|
|
mSystemReady = true;
|
|
}
|
|
|
|
protected void setCountryListener(final CountryListener listener) {
|
|
mHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mCountryDetector.setCountryListener(listener);
|
|
}
|
|
});
|
|
}
|
|
|
|
// For testing
|
|
boolean isSystemReady() {
|
|
return mSystemReady;
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
@Override
|
|
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
|
|
|
if (!DEBUG) return;
|
|
try {
|
|
final Printer p = new PrintWriterPrinter(fout);
|
|
p.println("CountryDetectorService state:");
|
|
p.println(" Number of listeners=" + mReceivers.keySet().size());
|
|
if (mCountryDetector == null) {
|
|
p.println(" ComprehensiveCountryDetector not initialized");
|
|
} else {
|
|
p.println(" " + mCountryDetector.toString());
|
|
}
|
|
} catch (Exception e) {
|
|
Slog.e(TAG, "Failed to dump CountryDetectorService: ", e);
|
|
}
|
|
}
|
|
}
|