1/*
2 * Copyright (C) 2016 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 com.android.printservice.recommendation;
18
19import android.annotation.IntRange;
20import android.annotation.NonNull;
21import android.annotation.StringRes;
22import com.android.internal.util.Preconditions;
23
24/**
25 * Wrapper for a {@link PrintServicePlugin}, isolating issues with the plugin as good as possible
26 * from the {@link RecommendationServiceImpl service}.
27 */
28class RemotePrintServicePlugin implements PrintServicePlugin.PrinterDiscoveryCallback {
29    /** Lock for this object */
30    private final Object mLock = new Object();
31
32    /** The name of the print service. */
33    public final @StringRes int name;
34
35    /** If the print service if for more than a single vendor */
36    public final boolean recommendsMultiVendorService;
37
38    /** The package name of the full print service */
39    public final @NonNull CharSequence packageName;
40
41    /** Wrapped plugin */
42    private final @NonNull PrintServicePlugin mPlugin;
43
44    /** The number of printers discovered by the plugin */
45    private @IntRange(from = 0) int mNumPrinters;
46
47    /** If the plugin is started by not yet stopped */
48    private boolean isRunning;
49
50    /** Listener for changes to {@link #mNumPrinters}. */
51    private @NonNull OnChangedListener mListener;
52
53    /**
54     * Create a new remote for a {@link PrintServicePlugin plugin}.
55     *
56     * @param plugin                       The plugin to be wrapped
57     * @param listener                     The listener to be notified about changes in this plugin
58     * @param recommendsMultiVendorService If the plugin detects printers of more than a single
59     *                                     vendor
60     *
61     * @throws PluginException If the plugin has issues while caching basic stub properties
62     */
63    public RemotePrintServicePlugin(@NonNull PrintServicePlugin plugin,
64            @NonNull OnChangedListener listener, boolean recommendsMultiVendorService)
65            throws PluginException {
66        mListener = listener;
67        mPlugin = plugin;
68        this.recommendsMultiVendorService = recommendsMultiVendorService;
69
70        // We handle any throwable to isolate our self from bugs in the plugin code.
71        // Cache simple properties to avoid having to deal with exceptions later in the code.
72        try {
73            name = Preconditions.checkArgumentPositive(mPlugin.getName(), "name");
74            packageName = Preconditions.checkStringNotEmpty(mPlugin.getPackageName(),
75                    "packageName");
76        } catch (Throwable e) {
77            throw new PluginException(mPlugin, "Cannot cache simple properties ", e);
78        }
79
80        isRunning = false;
81    }
82
83    /**
84     * Start the plugin. From now on there might be callbacks to the registered listener.
85     */
86    public void start()
87            throws PluginException {
88        // We handle any throwable to isolate our self from bugs in the stub code
89        try {
90            synchronized (mLock) {
91                isRunning = true;
92                mPlugin.start(this);
93            }
94        } catch (Throwable e) {
95            throw new PluginException(mPlugin, "Cannot start", e);
96        }
97    }
98
99    /**
100     * Stop the plugin. From this call on there will not be any more callbacks.
101     */
102    public void stop() throws PluginException {
103        // We handle any throwable to isolate our self from bugs in the stub code
104        try {
105            synchronized (mLock) {
106                mPlugin.stop();
107                isRunning = false;
108            }
109        } catch (Throwable e) {
110            throw new PluginException(mPlugin, "Cannot stop", e);
111        }
112    }
113
114    /**
115     * Get the current number of printers reported by the stub.
116     *
117     * @return The number of printers reported by the stub.
118     */
119    public @IntRange(from = 0) int getNumPrinters() {
120        return mNumPrinters;
121    }
122
123    @Override
124    public void onChanged(@IntRange(from = 0) int numDiscoveredPrinters) {
125        synchronized (mLock) {
126            Preconditions.checkState(isRunning);
127
128            mNumPrinters = Preconditions.checkArgumentNonnegative(numDiscoveredPrinters,
129                    "numDiscoveredPrinters");
130
131            if (mNumPrinters > 0) {
132                mListener.onChanged();
133            }
134        }
135    }
136
137    /**
138     * Listener to listen for changes to {@link #getNumPrinters}
139     */
140    public interface OnChangedListener {
141        void onChanged();
142    }
143
144    /**
145     * Exception thrown if the stub has any issues.
146     */
147    public class PluginException extends Exception {
148        private PluginException(PrintServicePlugin plugin, String message, Throwable e) {
149            super(plugin + ": " + message, e);
150        }
151    }
152}
153