diff options
| author | Philip P. Moltmann <moltmann@google.com> | 2016-03-14 14:31:12 -0700 |
|---|---|---|
| committer | Philip P. Moltmann <moltmann@google.com> | 2016-03-30 17:21:07 -0700 |
| commit | 9dcb86a48d73f399fb1b5c020005d76d350eeac2 (patch) | |
| tree | 710838ed3fd0c566be95dc52367cbc979dc9dbb5 /core/java/android | |
| parent | 394d3dfb2d9ccd2ca2fd7aad06ef2e9a8458a0c7 (diff) | |
Add the print service recommendation service
This service connects through the print manager to the print spooler:
PrintSpooler.AddPrintersActivity <-> PrintManager <-> PrintManagerService <-> UserState <-> RemotePrintServiceRecommendationService <-> PrintRecommendationService <-> PrintRecommendationServiceImpl
Hence there is a lot of mindless plumming.
The actual changes are only in the AddPrintersActivity which is extended
to show another list of services: The recommended services.
The PrintServiceRecommendationService is based on the experimenal print
service stubs provider. This provider was contributed the Android by
Mopria. As this services uses Android own network discovery service most
code from the experimental provider goes away. In fact the only logic
left over is the selections of mdns-txt fields to look at and the
printer vendor configuration.
This relies on the Android MDNS to get fixed (Bug: 27696905). This also
does not deal with how to update the recommendation service.
Bug: 24533249
Change-Id: I6edc6e25fc08a50d478b61c71bb8ea158b08624c
Diffstat (limited to 'core/java/android')
10 files changed, 696 insertions, 9 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; + } + } + } +} |
