diff options
| author | Philip P. Moltmann <moltmann@google.com> | 2016-03-31 01:33:15 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-03-31 01:33:17 +0000 |
| commit | 740a5f023eea7b2fdb3e31efe8b8d5ac18aa8302 (patch) | |
| tree | fd1f11d0a5173e59bd8451d633b87ad6bc1a40ec /core/java | |
| parent | bc2294b3c2503105c37d8de4a8cd825189199868 (diff) | |
| parent | 9dcb86a48d73f399fb1b5c020005d76d350eeac2 (diff) | |
Merge "Add the print service recommendation service" into nyc-dev
Diffstat (limited to 'core/java')
11 files changed, 719 insertions, 12 deletions
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 5eb8cc2f37a4..d7c267b5ca63 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -24,9 +24,11 @@ import android.print.IPrintDocumentAdapter; import android.print.PrintJobId; import android.print.IPrintJobStateChangeListener; import android.print.IPrintServicesChangeListener; +import android.printservice.recommendation.IRecommendationsChangeListener; import android.print.PrinterId; import android.print.PrintJobInfo; import android.print.PrintAttributes; +import android.printservice.recommendation.RecommendationInfo; import android.printservice.PrintServiceInfo; /** @@ -73,7 +75,6 @@ interface IPrintManager { * Get the print services. * * @param selectionFlags flags selecting which services to get - * @param selectedService if not null, the id of the print service to get * @param userId the id of the user requesting the services * * @return the list of selected print services. @@ -89,6 +90,37 @@ interface IPrintManager { */ void setPrintServiceEnabled(in ComponentName service, boolean isEnabled, int userId); + /** + * Listen for changes to the print service recommendations. + * + * @param listener the listener to add + * @param userId the id of the user listening + * + * @see android.print.PrintManager#getPrintServiceRecommendations + */ + void addPrintServiceRecommendationsChangeListener(in IRecommendationsChangeListener listener, + int userId); + + /** + * Stop listening for changes to the print service recommendations. + * + * @param listener the listener to remove + * @param userId the id of the user requesting the removal + * + * @see android.print.PrintManager#getPrintServiceRecommendations + */ + void removePrintServiceRecommendationsChangeListener(in IRecommendationsChangeListener listener, + int userId); + + /** + * Get the print service recommendations. + * + * @param userId the id of the user requesting the recommendations + * + * @return the list of selected print services. + */ + List<RecommendationInfo> getPrintServiceRecommendations(int userId); + void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); void startPrinterDiscovery(in IPrinterDiscoveryObserver observer, in List<PrinterId> priorityList, int userId); diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 25fc968fec5a..71f0bd615206 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -36,12 +36,15 @@ import android.os.RemoteException; import android.print.PrintDocumentAdapter.LayoutResultCallback; import android.print.PrintDocumentAdapter.WriteResultCallback; import android.printservice.PrintServiceInfo; +import android.printservice.recommendation.IRecommendationsChangeListener; +import android.printservice.recommendation.RecommendationInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.os.SomeArgs; +import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.lang.ref.WeakReference; @@ -113,6 +116,7 @@ public final class PrintManager { private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1; private static final int MSG_NOTIFY_PRINT_SERVICES_CHANGED = 2; + private static final int MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED = 3; /** * Package name of print spooler. @@ -202,6 +206,9 @@ public final class PrintManager { mPrintJobStateChangeListeners; private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper> mPrintServicesChangeListeners; + private Map<PrintServiceRecommendationsChangeListener, + PrintServiceRecommendationsChangeListenerWrapper> + mPrintServiceRecommendationsChangeListeners; /** @hide */ public interface PrintJobStateChangeListener { @@ -223,6 +230,15 @@ public final class PrintManager { public void onPrintServicesChanged(); } + /** @hide */ + public interface PrintServiceRecommendationsChangeListener { + + /** + * Callback notifying that the print service recommendations changed. + */ + void onPrintServiceRecommendationsChanged(); + } + /** * Creates a new instance. * @@ -260,7 +276,14 @@ public final class PrintManager { listener.onPrintServicesChanged(); } } break; - + case MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED: { + PrintServiceRecommendationsChangeListenerWrapper wrapper = + (PrintServiceRecommendationsChangeListenerWrapper) message.obj; + PrintServiceRecommendationsChangeListener listener = wrapper.getListener(); + if (listener != null) { + listener.onPrintServiceRecommendationsChanged(); + } + } break; } } }; @@ -539,13 +562,14 @@ public final class PrintManager { * @see android.print.PrintManager#getPrintServices */ void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) { + Preconditions.checkNotNull(listener); + if (mService == null) { Log.w(LOG_TAG, "Feature android.software.print not available"); return; } if (mPrintServicesChangeListeners == null) { - mPrintServicesChangeListeners = new ArrayMap<PrintServicesChangeListener, - PrintServicesChangeListenerWrapper>(); + mPrintServicesChangeListeners = new ArrayMap<>(); } PrintServicesChangeListenerWrapper wrappedListener = new PrintServicesChangeListenerWrapper(listener, mHandler); @@ -565,6 +589,8 @@ public final class PrintManager { * @see android.print.PrintManager#getPrintServices */ void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) { + Preconditions.checkNotNull(listener); + if (mService == null) { Log.w(LOG_TAG, "Feature android.software.print not available"); return; @@ -588,7 +614,6 @@ public final class PrintManager { } } - /** * Gets the list of print services, but does not register for updates. The user has to register * for updates by itself, or use {@link PrintServicesLoader}. @@ -596,7 +621,7 @@ public final class PrintManager { * @param selectionFlags flags selecting which services to get. Either * {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both. * - * @return The enabled service list or an empty list. + * @return The print service list or an empty list. * * @see #addPrintServicesChangeListener(PrintServicesChangeListener) * @see #removePrintServicesChangeListener(PrintServicesChangeListener) @@ -604,6 +629,8 @@ public final class PrintManager { * @hide */ public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) { + Preconditions.checkFlagsArgument(selectionFlags, ALL_SERVICES); + try { List<PrintServiceInfo> services = mService.getPrintServices(selectionFlags, mUserId); if (services != null) { @@ -616,6 +643,92 @@ public final class PrintManager { } /** + * Listen for changes to the print service recommendations. + * + * @param listener the listener to add + * + * @see android.print.PrintManager#getPrintServiceRecommendations + */ + void addPrintServiceRecommendationsChangeListener( + @NonNull PrintServiceRecommendationsChangeListener listener) { + Preconditions.checkNotNull(listener); + + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } + if (mPrintServiceRecommendationsChangeListeners == null) { + mPrintServiceRecommendationsChangeListeners = new ArrayMap<>(); + } + PrintServiceRecommendationsChangeListenerWrapper wrappedListener = + new PrintServiceRecommendationsChangeListenerWrapper(listener, mHandler); + try { + mService.addPrintServiceRecommendationsChangeListener(wrappedListener, mUserId); + mPrintServiceRecommendationsChangeListeners.put(listener, wrappedListener); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Stop listening for changes to the print service recommendations. + * + * @param listener the listener to remove + * + * @see android.print.PrintManager#getPrintServiceRecommendations + */ + void removePrintServiceRecommendationsChangeListener( + @NonNull PrintServiceRecommendationsChangeListener listener) { + Preconditions.checkNotNull(listener); + + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } + if (mPrintServiceRecommendationsChangeListeners == null) { + return; + } + PrintServiceRecommendationsChangeListenerWrapper wrappedListener = + mPrintServiceRecommendationsChangeListeners.remove(listener); + if (wrappedListener == null) { + return; + } + if (mPrintServiceRecommendationsChangeListeners.isEmpty()) { + mPrintServiceRecommendationsChangeListeners = null; + } + wrappedListener.destroy(); + try { + mService.removePrintServiceRecommendationsChangeListener(wrappedListener, mUserId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Gets the list of print service recommendations, but does not register for updates. The user + * has to register for updates by itself, or use {@link PrintServiceRecommendationsLoader}. + * + * @return The print service recommendations list or an empty list. + * + * @see #addPrintServiceRecommendationsChangeListener + * @see #removePrintServiceRecommendationsChangeListener + * + * @hide + */ + public @NonNull List<RecommendationInfo> getPrintServiceRecommendations() { + try { + List<RecommendationInfo> recommendations = + mService.getPrintServiceRecommendations(mUserId); + if (recommendations != null) { + return recommendations; + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + return Collections.emptyList(); + } + + /** * @hide */ public PrinterDiscoverySession createPrinterDiscoverySession() { @@ -1242,4 +1355,37 @@ public final class PrintManager { return mWeakListener.get(); } } + + /** + * @hide + */ + public static final class PrintServiceRecommendationsChangeListenerWrapper extends + IRecommendationsChangeListener.Stub { + private final WeakReference<PrintServiceRecommendationsChangeListener> mWeakListener; + private final WeakReference<Handler> mWeakHandler; + + public PrintServiceRecommendationsChangeListenerWrapper( + PrintServiceRecommendationsChangeListener listener, Handler handler) { + mWeakListener = new WeakReference<>(listener); + mWeakHandler = new WeakReference<>(handler); + } + + @Override + public void onRecommendationsChanged() { + Handler handler = mWeakHandler.get(); + PrintServiceRecommendationsChangeListener listener = mWeakListener.get(); + if (handler != null && listener != null) { + handler.obtainMessage(MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED, + this).sendToTarget(); + } + } + + public void destroy() { + mWeakListener.clear(); + } + + public PrintServiceRecommendationsChangeListener getListener() { + return mWeakListener.get(); + } + } } diff --git a/core/java/android/print/PrintServiceRecommendationsLoader.java b/core/java/android/print/PrintServiceRecommendationsLoader.java new file mode 100644 index 000000000000..bb5d065c6430 --- /dev/null +++ b/core/java/android/print/PrintServiceRecommendationsLoader.java @@ -0,0 +1,121 @@ +/* + * 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 android.print; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.Loader; +import android.os.Handler; +import android.os.Message; +import android.printservice.recommendation.RecommendationInfo; +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Loader for the list of print service recommendations. + * + * @hide + */ +public class PrintServiceRecommendationsLoader extends Loader<List<RecommendationInfo>> { + /** The print manager to be used by this object */ + private final @NonNull PrintManager mPrintManager; + + /** Handler to sequentialize the delivery of the results to the main thread */ + private final Handler mHandler; + + /** Listens for updates to the data from the platform */ + private PrintManager.PrintServiceRecommendationsChangeListener mListener; + + /** + * Create a new PrintServicesLoader. + * + * @param printManager The print manager supplying the data + * @param context Context of the using object + */ + public PrintServiceRecommendationsLoader(@NonNull PrintManager printManager, + @NonNull Context context) { + super(Preconditions.checkNotNull(context)); + mHandler = new MyHandler(); + mPrintManager = Preconditions.checkNotNull(printManager); + } + + @Override + protected void onForceLoad() { + queueNewResult(); + } + + /** + * Read the print service recommendations and queue it to be delivered on the main thread. + */ + private void queueNewResult() { + Message m = mHandler.obtainMessage(0); + m.obj = mPrintManager.getPrintServiceRecommendations(); + mHandler.sendMessage(m); + } + + @Override + protected void onStartLoading() { + mListener = new PrintManager.PrintServiceRecommendationsChangeListener() { + @Override + public void onPrintServiceRecommendationsChanged() { + queueNewResult(); + } + }; + + mPrintManager.addPrintServiceRecommendationsChangeListener(mListener); + + // Immediately deliver a result + deliverResult(mPrintManager.getPrintServiceRecommendations()); + } + + @Override + protected void onStopLoading() { + if (mListener != null) { + mPrintManager.removePrintServiceRecommendationsChangeListener(mListener); + mListener = null; + } + + if (mHandler != null) { + mHandler.removeMessages(0); + } + } + + @Override + protected void onReset() { + onStopLoading(); + } + + /** + * Handler to sequentialize all the updates to the main thread. + */ + private class MyHandler extends Handler { + /** + * Create a new handler on the main thread. + */ + public MyHandler() { + super(getContext().getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + if (isStarted()) { + deliverResult((List<RecommendationInfo>) msg.obj); + } + } + } +} diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java index ed411141d1fb..60d7d666c2c9 100644 --- a/core/java/android/print/PrintServicesLoader.java +++ b/core/java/android/print/PrintServicesLoader.java @@ -22,6 +22,7 @@ import android.content.Loader; import android.os.Handler; import android.os.Message; import android.printservice.PrintServiceInfo; +import com.android.internal.util.Preconditions; import java.util.List; @@ -46,13 +47,16 @@ public class PrintServicesLoader extends Loader<List<PrintServiceInfo>> { /** * Create a new PrintServicesLoader. * + * @param printManager The print manager supplying the data + * @param context Context of the using object * @param selectionFlags What type of services to load. */ public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context, int selectionFlags) { - super(context); - mPrintManager = printManager; - mSelectionFlags = selectionFlags; + super(Preconditions.checkNotNull(context)); + mPrintManager = Preconditions.checkNotNull(printManager); + mSelectionFlags = Preconditions.checkFlagsArgument(selectionFlags, + PrintManager.ALL_SERVICES); } @Override diff --git a/core/java/android/printservice/recommendation/IRecommendationService.aidl b/core/java/android/printservice/recommendation/IRecommendationService.aidl new file mode 100644 index 000000000000..ce9ea6fd9fcb --- /dev/null +++ b/core/java/android/printservice/recommendation/IRecommendationService.aidl @@ -0,0 +1,30 @@ +/* + * 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 android.printservice.recommendation; + +import android.printservice.recommendation.IRecommendationServiceCallbacks; + +/** + * Interface for communication with the print service recommendation service. + * + * @see android.print.IPrintServiceRecommendationServiceCallbacks + * + * @hide + */ +oneway interface IRecommendationService { + void registerCallbacks(in IRecommendationServiceCallbacks callbacks); +} diff --git a/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl b/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl new file mode 100644 index 000000000000..95286544eed0 --- /dev/null +++ b/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl @@ -0,0 +1,35 @@ +/* + * 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 android.printservice.recommendation; + +import android.printservice.recommendation.RecommendationInfo; + +/** + * Callbacks for communication with the print service recommendation service. + * + * @see android.print.IPrintServiceRecommendationService + * + * @hide + */ +oneway interface IRecommendationServiceCallbacks { + /** + * Update the print service recommendations. + * + * @param recommendations the new print service recommendations + */ + void onRecommendationsUpdated(in List<RecommendationInfo> recommendations); +} diff --git a/core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl b/core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl new file mode 100644 index 000000000000..8ca5c69e8180 --- /dev/null +++ b/core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl @@ -0,0 +1,26 @@ +/* + * 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 android.printservice.recommendation; + +/** + * Interface for observing changes of the print service recommendations. + * + * @hide + */ +oneway interface IRecommendationsChangeListener { + void onRecommendationsChanged(); +} diff --git a/core/java/android/printservice/recommendation/RecommendationInfo.aidl b/core/java/android/printservice/recommendation/RecommendationInfo.aidl new file mode 100644 index 000000000000..f21d0bf3f584 --- /dev/null +++ b/core/java/android/printservice/recommendation/RecommendationInfo.aidl @@ -0,0 +1,22 @@ +/** + * 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 android.printservice.recommendation; + +/** + * @hide + */ +parcelable RecommendationInfo; diff --git a/core/java/android/printservice/recommendation/RecommendationInfo.java b/core/java/android/printservice/recommendation/RecommendationInfo.java new file mode 100644 index 000000000000..65d534e45e1c --- /dev/null +++ b/core/java/android/printservice/recommendation/RecommendationInfo.java @@ -0,0 +1,133 @@ +/* + * 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 android.printservice.recommendation; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.printservice.PrintService; +import com.android.internal.util.Preconditions; + +/** + * A recommendation to install a {@link PrintService print service}. + * + * @hide + */ +@SystemApi +public final class RecommendationInfo implements Parcelable { + /** Package name of the print service. */ + private @NonNull final CharSequence mPackageName; + + /** Display name of the print service. */ + private @NonNull final CharSequence mName; + + /** Number of printers the print service would discover if installed. */ + private @IntRange(from = 0) final int mNumDiscoveredPrinters; + + /** If the service detects printer from multiple vendors. */ + private final boolean mRecommendsMultiVendorService; + + /** + * Create a new recommendation. + * + * @param packageName Package name of the print service + * @param name Display name of the print service + * @param numDiscoveredPrinters Number of printers the print service would discover if + * installed + * @param recommendsMultiVendorService If the service detects printer from multiple vendor + */ + public RecommendationInfo(@NonNull CharSequence packageName, @NonNull CharSequence name, + @IntRange(from = 0) int numDiscoveredPrinters, boolean recommendsMultiVendorService) { + mPackageName = Preconditions.checkStringNotEmpty(packageName); + mName = Preconditions.checkStringNotEmpty(name); + mNumDiscoveredPrinters = Preconditions.checkArgumentNonnegative(numDiscoveredPrinters); + mRecommendsMultiVendorService = recommendsMultiVendorService; + } + + /** + * Create a new recommendation from a parcel. + * + * @param parcel The parcel containing the data + * + * @see #CREATOR + */ + private RecommendationInfo(@NonNull Parcel parcel) { + this(parcel.readCharSequence(), parcel.readCharSequence(), parcel.readInt(), + parcel.readByte() != 0); + } + + /** + * @return The package name the recommendations recommends. + */ + public CharSequence getPackageName() { + return mPackageName; + } + + /** + * @return Whether the recommended print service detects printers of more than one vendor. + */ + public boolean recommendsMultiVendorService() { + return mRecommendsMultiVendorService; + } + + /** + * @return The number of printer the print service would detect. + */ + public int getNumDiscoveredPrinters() { + return mNumDiscoveredPrinters; + } + + /** + * @return The name of the recommended print service. + */ + public CharSequence getName() { + return mName; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mPackageName); + dest.writeCharSequence(mName); + dest.writeInt(mNumDiscoveredPrinters); + dest.writeByte((byte) (mRecommendsMultiVendorService ? 1 : 0)); + } + + /** + * Utility class used to create new print service recommendation objects from parcels. + * + * @see #RecommendationInfo(Parcel) + */ + public static final Creator<RecommendationInfo> CREATOR = + new Creator<RecommendationInfo>() { + @Override + public RecommendationInfo createFromParcel(Parcel in) { + return new RecommendationInfo(in); + } + + @Override + public RecommendationInfo[] newArray(int size) { + return new RecommendationInfo[size]; + } + }; +} diff --git a/core/java/android/printservice/recommendation/RecommendationService.java b/core/java/android/printservice/recommendation/RecommendationService.java new file mode 100644 index 000000000000..b7ea51271043 --- /dev/null +++ b/core/java/android/printservice/recommendation/RecommendationService.java @@ -0,0 +1,138 @@ +/* + * 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 android.printservice.recommendation; + +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import com.android.internal.annotations.GuardedBy; + +import java.util.List; + +/** + * Base class for the print service recommendation services. + * + * @hide + */ +@SystemApi +public abstract class RecommendationService extends Service { + private static final String LOG_TAG = "PrintServiceRecS"; + + /** Used to push onConnect and onDisconnect on the main thread */ + private Handler mHandler; + + /** + * The {@link Intent} action that must be declared as handled by a service in its manifest for + * the system to recognize it as a print service recommendation service. + * + * @hide + */ + public static final String SERVICE_INTERFACE = + "android.printservice.recommendation.RecommendationService"; + + /** Registered callbacks, only modified on main thread */ + private IRecommendationServiceCallbacks mCallbacks; + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + + mHandler = new MyHandler(); + } + + /** + * Update the print service recommendations. + * + * @param recommendations The new set of recommendations + */ + public final void updateRecommendations(@Nullable List<RecommendationInfo> recommendations) { + mHandler.obtainMessage(MyHandler.MSG_UPDATE, recommendations).sendToTarget(); + } + + @Override + public final IBinder onBind(Intent intent) { + return new IRecommendationService.Stub() { + @Override + public void registerCallbacks(IRecommendationServiceCallbacks callbacks) { + // The callbacks come in order of the caller on oneway calls. Hence while the caller + // cannot know at what time the connection is made, he can know the ordering of + // connection and disconnection. + // + // Similar he cannot know when the disconnection is processed, hence he has to + // handle callbacks after calling disconnect. + if (callbacks != null) { + mHandler.obtainMessage(MyHandler.MSG_CONNECT, callbacks).sendToTarget(); + } else { + mHandler.obtainMessage(MyHandler.MSG_DISCONNECT).sendToTarget(); + } + } + }; + } + + /** + * Called when the client connects to the recommendation service. + */ + public abstract void onConnected(); + + /** + * Called when the client disconnects from the recommendation service. + */ + public abstract void onDisconnected(); + + private class MyHandler extends Handler { + static final int MSG_CONNECT = 1; + static final int MSG_DISCONNECT = 2; + static final int MSG_UPDATE = 3; + + MyHandler() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_CONNECT: + mCallbacks = (IRecommendationServiceCallbacks) msg.obj; + onConnected(); + break; + case MSG_DISCONNECT: + onDisconnected(); + mCallbacks = null; + break; + case MSG_UPDATE: + // Note that there might be a connection change in progress. In this case the + // message is handled as before the change. This is acceptable as the caller of + // the connection change has not guarantee when the connection change binder + // transaction is actually processed. + try { + mCallbacks.onRecommendationsUpdated((List<RecommendationInfo>) msg.obj); + } catch (RemoteException | NullPointerException e) { + Log.e(LOG_TAG, "Could not update recommended services", e); + } + break; + } + } + } +} diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index c46851e1c757..7c8524671716 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -56,7 +56,7 @@ public class Preconditions { * @return the string reference that was validated * @throws IllegalArgumentException if {@code string} is empty */ - public static @NonNull String checkStringNotEmpty(final String string) { + public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) { if (TextUtils.isEmpty(string)) { throw new IllegalArgumentException(); } @@ -73,7 +73,7 @@ public class Preconditions { * @return the string reference that was validated * @throws IllegalArgumentException if {@code string} is empty */ - public static @NonNull String checkStringNotEmpty(final String string, + public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string, final Object errorMessage) { if (TextUtils.isEmpty(string)) { throw new IllegalArgumentException(String.valueOf(errorMessage)); @@ -141,13 +141,17 @@ public class Preconditions { /** * Check the requested flags, throwing if any requested flags are outside * the allowed set. + * + * @return the validated requested flags. */ - public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) { + public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) { if ((requestedFlags & allowedFlags) != requestedFlags) { throw new IllegalArgumentException("Requested flags 0x" + Integer.toHexString(requestedFlags) + ", but only 0x" + Integer.toHexString(allowedFlags) + " are allowed"); } + + return requestedFlags; } /** @@ -170,6 +174,22 @@ public class Preconditions { /** * Ensures that that the argument numeric value is non-negative. * + * @param value a numeric int value + * + * @return the validated numeric value + * @throws IllegalArgumentException if {@code value} was negative + */ + public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) { + if (value < 0) { + throw new IllegalArgumentException(); + } + + return value; + } + + /** + * Ensures that that the argument numeric value is non-negative. + * * @param value a numeric long value * @param errorMessage the exception message to use if the check fails * @return the validated numeric value |
