Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 853a6f56 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Add a alert that allows the user to approve a print service when

we print using it the first time.

This warning used to be shown when the print settings app was used
to enable a service.

If two warning as shown for the same print service we automcatially
dismiss all dialogs once one dialog is confirmed. Please note that
we are not confirming the printjob as it is unexpeced to have a
single click to confirm multiple print jobs.

Change-Id: I8bb0a49bac2063c1c55e2f24bd34df2c44e2df89
Bug: 24135353
parent f789ef7e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -46,4 +46,5 @@ oneway interface IPrintSpooler {
    void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
    void setClient(IPrintSpoolerClient client);
    void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
    void removeApprovedPrintService(in ComponentName serviceToRemove);
}
+12 −0
Original line number Diff line number Diff line
@@ -187,6 +187,18 @@
    <!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
    <string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> &#8211; unavailable</string>

    <!-- Title for a warning message about security implications of using a print service,
         displayed as a dialog message when the user prints using a print service that has not been
         used before. [CHAR LIMIT=NONE] -->
    <string name="print_service_security_warning_title">Use
         <xliff:g id="service" example="My Print Service">%1$s</xliff:g>?</string>

    <!-- Summary for a warning message about security implications of using a print service,
         displayed as a dialog message when the user prints using a print service that has not been
         used before. [CHAR LIMIT=NONE] -->
    <string name="print_service_security_warning_summary">Your document may pass through one or
         more servers on its way to the printer.</string>

    <!-- Arrays -->

    <!-- Color mode labels. -->
+19 −3
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
import com.android.internal.util.FastXmlSerializer;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;

import libcore.io.IoUtils;

@@ -67,6 +68,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Service for exposing some of the {@link PrintSpooler} functionality to
@@ -136,10 +138,10 @@ public final class PrintSpoolerService extends Service {

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        synchronized (mLock) {
        String prefix = (args.length > 0) ? args[0] : "";
        String tab = "  ";

        synchronized (mLock) {
            pw.append(prefix).append("print jobs:").println();
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
@@ -160,6 +162,14 @@ public final class PrintSpoolerService extends Service {
                }
            }
        }

        pw.append(prefix).append("approved print services:").println();
        Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
        if (approvedPrintServices != null) {
            for (String approvedService : approvedPrintServices) {
                pw.append(prefix).append(tab).append(approvedService).println();
            }
        }
    }

    private void sendOnPrintJobQueued(PrintJobInfo printJob) {
@@ -1307,6 +1317,12 @@ public final class PrintSpoolerService extends Service {
            PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
        }

        @Override
        public void removeApprovedPrintService(ComponentName serviceToRemove) {
            (new ApprovedPrintServices(PrintSpoolerService.this))
                    .removeApprovedService(serviceToRemove);
        }

        public PrintSpoolerService getService() {
            return PrintSpoolerService.this;
        }
+123 −1
Original line number Diff line number Diff line
@@ -17,14 +17,21 @@
package com.android.printspooler.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -81,6 +88,7 @@ import com.android.printspooler.model.RemotePrintDocument;
import com.android.printspooler.model.RemotePrintDocument.RemotePrintDocumentInfo;
import com.android.printspooler.renderer.IPdfEditor;
import com.android.printspooler.renderer.PdfManipulationService;
import com.android.printspooler.util.ApprovedPrintServices;
import com.android.printspooler.util.MediaSizeUtils;
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
import com.android.printspooler.util.PageRangeUtils;
@@ -88,6 +96,7 @@ import com.android.printspooler.util.PrintOptionUtils;
import com.android.printspooler.widget.PrintContentView;
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
import com.android.printspooler.widget.PrintContentView.OptionsStateController;

import libcore.io.IoUtils;
import libcore.io.Streams;

@@ -1184,12 +1193,125 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
        mPrintButton.setOnClickListener(clickListener);
    }

    /**
     * A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
     * dismissed if the same {@link PrintService} gets approved by another
     * {@link PrintServiceApprovalDialog}.
     */
    private static final class PrintServiceApprovalDialog extends DialogFragment
            implements OnSharedPreferenceChangeListener {
        private static final String PRINTSERVICE_KEY = "PRINTSERVICE";
        private ApprovedPrintServices mApprovedServices;

        /**
         * Create a new {@link PrintServiceApprovalDialog} that ask the user to approve a
         * {@link PrintService}.
         *
         * @param printService The {@link ComponentName} of the service to approve
         * @return A new {@link PrintServiceApprovalDialog} that might approve the service
         */
        static PrintServiceApprovalDialog newInstance(ComponentName printService) {
            PrintServiceApprovalDialog dialog = new PrintServiceApprovalDialog();

            Bundle args = new Bundle();
            args.putParcelable(PRINTSERVICE_KEY, printService);
            dialog.setArguments(args);

            return dialog;
        }

        @Override
        public void onStop() {
            super.onStop();

            mApprovedServices.unregisterChangeListener(this);
        }

        @Override
        public void onStart() {
            super.onStart();

            ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
            synchronized (ApprovedPrintServices.sLock) {
                if (mApprovedServices.isApprovedService(printService)) {
                    dismiss();
                } else {
                    mApprovedServices.registerChangeListenerLocked(this);
                }
            }
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            super.onCreateDialog(savedInstanceState);

            mApprovedServices = new ApprovedPrintServices(getActivity());

            PackageManager packageManager = getActivity().getPackageManager();
            CharSequence serviceLabel;
            try {
                ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);

                serviceLabel = packageManager.getApplicationInfo(printService.getPackageName(), 0)
                        .loadLabel(packageManager);
            } catch (NameNotFoundException e) {
                serviceLabel = null;
            }

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(getString(R.string.print_service_security_warning_title,
                    serviceLabel))
                    .setMessage(getString(R.string.print_service_security_warning_summary,
                            serviceLabel))
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            ComponentName printService =
                                    getArguments().getParcelable(PRINTSERVICE_KEY);
                            // Prevent onSharedPreferenceChanged from getting triggered
                            mApprovedServices
                                    .unregisterChangeListener(PrintServiceApprovalDialog.this);

                            mApprovedServices.addApprovedService(printService);
                            ((PrintActivity) getActivity()).confirmPrint();
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, null);

            return builder.create();
        }

        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);

            synchronized (ApprovedPrintServices.sLock) {
                if (mApprovedServices.isApprovedService(printService)) {
                    dismiss();
                }
            }
        }
    }

    private final class MyClickListener implements OnClickListener {
        @Override
        public void onClick(View view) {
            if (view == mPrintButton) {
                if (mCurrentPrinter != null) {
                    if (mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter) {
                        confirmPrint();
                    } else {
                        ApprovedPrintServices approvedServices =
                                new ApprovedPrintServices(PrintActivity.this);

                        ComponentName printService = mCurrentPrinter.getId().getServiceName();
                        if (approvedServices.isApprovedService(printService)) {
                            confirmPrint();
                        } else {
                            PrintServiceApprovalDialog.newInstance(printService)
                                    .show(getFragmentManager(), "approve");
                        }
                    }
                } else {
                    cancelPrint();
                }
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.printspooler.util;

import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.printservice.PrintService;
import android.util.ArraySet;

import java.util.Set;

/**
 * Manage approved print services. These services are stored in the shared preferences.
 */
public class ApprovedPrintServices {
    /**
     * Used for locking accesses to the approved services.
     */
    static final public Object sLock = new Object();

    private static final String APPROVED_SERVICES_PREFERENCE = "PRINT_SPOOLER_APPROVED_SERVICES";
    private final SharedPreferences mPreferences;

    /**
     * Create a new {@link ApprovedPrintServices}
     *
     * @param owner The {@link Context} using this object.
     */
    public ApprovedPrintServices(Context owner) {
        mPreferences = owner.getSharedPreferences(APPROVED_SERVICES_PREFERENCE,
                Context.MODE_PRIVATE);
    }

    /**
     * Get {@link Set} of approved services.
     *
     * @return A {@link Set} containing all currently approved services.
     */
    public Set<String> getApprovedServices() {
        return mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
    }

    /**
     * Check if a {@link PrintService} is approved.
     *
     * This function does not acquire the {@link #sLock}.
     *
     * @param service The {@link ComponentName} of the {@link PrintService} that might be approved
     * @return true iff the service is currently approved
     */
    public boolean isApprovedService(ComponentName service) {
        final Set<String> approvedServices = getApprovedServices();

        if (approvedServices != null) {
            final String flattenedString = service.flattenToShortString();

            for (String approvedService : approvedServices) {
                if (approvedService.equals(flattenedString)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Add a {@link PrintService} to the list of approved print services.
     *
     * @param serviceToAdd The {@link ComponentName} of the {@link PrintService} to be approved.
     */
    public void addApprovedService(ComponentName serviceToAdd) {
        synchronized (sLock) {
            Set<String> oldApprovedServices =
                    mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);

            Set<String> newApprovedServices;
            if (oldApprovedServices == null) {
                newApprovedServices = new ArraySet<String>(1);
            } else {
                // Copy approved services.
                newApprovedServices = new ArraySet<String>(oldApprovedServices);
            }
            newApprovedServices.add(serviceToAdd.flattenToShortString());

            SharedPreferences.Editor editor = mPreferences.edit();
            editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
            editor.apply();
        }
    }

    /**
     * Add a {@link OnSharedPreferenceChangeListener} that listens for changes to the approved
     * services. Should only be called while holding {@link #sLock} to synchronize against
     * {@link #addApprovedService}.
     *
     * @param listener {@link OnSharedPreferenceChangeListener} to register
     */
    public void registerChangeListenerLocked(OnSharedPreferenceChangeListener listener) {
        mPreferences.registerOnSharedPreferenceChangeListener(listener);
    }

    /**
     * Unregister a listener registered in {@link #registerChangeListenerLocked}.
     *
     * @param listener {@link OnSharedPreferenceChangeListener} to unregister
     */
    public void unregisterChangeListener(OnSharedPreferenceChangeListener listener) {
        mPreferences.unregisterOnSharedPreferenceChangeListener(listener);
    }

    /**
     * If a {@link PrintService} is approved, remove it from the list of approved services.
     *
     * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
     */
    public void removeApprovedService(ComponentName serviceToRemove) {
        synchronized (sLock) {
            if (isApprovedService(serviceToRemove)) {
                // Copy approved services.
                ArraySet<String> approvedServices = new ArraySet<String>(
                        mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));

                SharedPreferences.Editor editor = mPreferences.edit();

                final int numApprovedServices = approvedServices.size();
                for (int i = 0; i < numApprovedServices; i++) {
                    if (approvedServices.valueAt(i)
                            .equals(serviceToRemove.flattenToShortString())) {
                        approvedServices.removeAt(i);
                        break;
                    }
                }

                editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
                editor.apply();
            }
        }
    }
}
Loading