1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package javax.crypto; 28 29 import java.util.*; 30 31 import java.security.*; 32 import java.security.Provider.Service; 33 import java.security.spec.AlgorithmParameterSpec; 34 35 import java.nio.ByteBuffer; 36 37 /* Android-removed: this debugging mechanism is not used in Android. 38 import sun.security.util.Debug; 39 */ 40 import sun.security.jca.*; 41 import sun.security.jca.GetInstance.Instance; 42 43 /** 44 * This class provides the functionality of a "Message Authentication Code" 45 * (MAC) algorithm. 46 * 47 * <p> A MAC provides a way to check 48 * the integrity of information transmitted over or stored in an unreliable 49 * medium, based on a secret key. Typically, message 50 * authentication codes are used between two parties that share a secret 51 * key in order to validate information transmitted between these 52 * parties. 53 * 54 * <p> A MAC mechanism that is based on cryptographic hash functions is 55 * referred to as HMAC. HMAC can be used with any cryptographic hash function, 56 * e.g., MD5 or SHA-1, in combination with a secret shared key. HMAC is 57 * specified in RFC 2104. 58 * 59 * <p> Android provides the following <code>Mac</code> algorithms: 60 * <table> 61 * <thead> 62 * <tr> 63 * <th>Algorithm</th> 64 * <th>Supported API Levels</th> 65 * </tr> 66 * </thead> 67 * <tbody> 68 * <tr class="deprecated"> 69 * <td>DESMAC</td> 70 * <td>1-8</td> 71 * </tr> 72 * <tr class="deprecated"> 73 * <td>DESMAC/CFB8</td> 74 * <td>1-8</td> 75 * </tr> 76 * <tr class="deprecated"> 77 * <td>DESedeMAC</td> 78 * <td>1-8</td> 79 * </tr> 80 * <tr class="deprecated"> 81 * <td>DESedeMAC/CFB8</td> 82 * <td>1-8</td> 83 * </tr> 84 * <tr class="deprecated"> 85 * <td>DESedeMAC64</td> 86 * <td>1-8</td> 87 * </tr> 88 * <tr class="deprecated"> 89 * <td>DESwithISO9797</td> 90 * <td>1-8</td> 91 * </tr> 92 * <tr> 93 * <td>HmacMD5</td> 94 * <td>1+</td> 95 * </tr> 96 * <tr> 97 * <td>HmacSHA1</td> 98 * <td>1+</td> 99 * </tr> 100 * <tr> 101 * <td>HmacSHA224</td> 102 * <td>1-8,22+</td> 103 * </tr> 104 * <tr> 105 * <td>HmacSHA256</td> 106 * <td>1+</td> 107 * </tr> 108 * <tr> 109 * <td>HmacSHA384</td> 110 * <td>1+</td> 111 * </tr> 112 * <tr> 113 * <td>HmacSHA512</td> 114 * <td>1+</td> 115 * </tr> 116 * <tr class="deprecated"> 117 * <td>ISO9797ALG3MAC</td> 118 * <td>1-8</td> 119 * </tr> 120 * <tr> 121 * <td>PBEwithHmacSHA</td> 122 * <td>1+</td> 123 * </tr> 124 * <tr> 125 * <td>PBEwithHmacSHA1</td> 126 * <td>1+</td> 127 * </tr> 128 * <tr> 129 * <td>PBEwithHmacSHA224</td> 130 * <td>26+</td> 131 * </tr> 132 * <tr> 133 * <td>PBEwithHmacSHA256</td> 134 * <td>26+</td> 135 * </tr> 136 * <tr> 137 * <td>PBEwithHmacSHA384</td> 138 * <td>26+</td> 139 * </tr> 140 * <tr> 141 * <td>PBEwithHmacSHA512</td> 142 * <td>26+</td> 143 * </tr> 144 * </tbody> 145 * </table> 146 * 147 * These algorithms are described in the 148 * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 149 * Mac section</a> of the 150 * Java Cryptography Architecture Standard Algorithm Name Documentation. 151 * 152 * @author Jan Luehe 153 * 154 * @since 1.4 155 */ 156 157 public class Mac implements Cloneable { 158 159 /* Android-removed: this debugging mechanism is not used in Android. 160 private static final Debug pdebug = 161 Debug.getInstance("provider", "Provider"); 162 private static final boolean skipDebug = 163 Debug.isOn("engine=") && !Debug.isOn("mac"); 164 */ 165 166 // The provider 167 private Provider provider; 168 169 // The provider implementation (delegate) 170 private MacSpi spi; 171 172 // The name of the MAC algorithm. 173 private final String algorithm; 174 175 // Has this object been initialized? 176 private boolean initialized = false; 177 178 private final Object lock; 179 180 /** 181 * Creates a MAC object. 182 * 183 * @param macSpi the delegate 184 * @param provider the provider 185 * @param algorithm the algorithm 186 */ Mac(MacSpi macSpi, Provider provider, String algorithm)187 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 188 this.spi = macSpi; 189 this.provider = provider; 190 this.algorithm = algorithm; 191 lock = null; 192 } 193 Mac(String algorithm)194 private Mac(String algorithm) { 195 this.algorithm = algorithm; 196 lock = new Object(); 197 } 198 199 /** 200 * Returns the algorithm name of this <code>Mac</code> object. 201 * 202 * <p>This is the same name that was specified in one of the 203 * <code>getInstance</code> calls that created this 204 * <code>Mac</code> object. 205 * 206 * @return the algorithm name of this <code>Mac</code> object. 207 */ getAlgorithm()208 public final String getAlgorithm() { 209 return this.algorithm; 210 } 211 212 /** 213 * Returns a <code>Mac</code> object that implements the 214 * specified MAC algorithm. 215 * 216 * <p> This method traverses the list of registered security Providers, 217 * starting with the most preferred Provider. 218 * A new Mac object encapsulating the 219 * MacSpi implementation from the first 220 * Provider that supports the specified algorithm is returned. 221 * 222 * <p> Note that the list of registered providers may be retrieved via 223 * the {@link Security#getProviders() Security.getProviders()} method. 224 * 225 * @param algorithm the standard name of the requested MAC algorithm. 226 * See the Mac section in the <a href= 227 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 228 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 229 * for information about standard algorithm names. 230 * 231 * @return the new <code>Mac</code> object. 232 * 233 * @exception NoSuchAlgorithmException if no Provider supports a 234 * MacSpi implementation for the 235 * specified algorithm. 236 * 237 * @see java.security.Provider 238 */ getInstance(String algorithm)239 public static final Mac getInstance(String algorithm) 240 throws NoSuchAlgorithmException { 241 List<Service> services = GetInstance.getServices("Mac", algorithm); 242 // make sure there is at least one service from a signed provider 243 Iterator<Service> t = services.iterator(); 244 while (t.hasNext()) { 245 Service s = t.next(); 246 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 247 continue; 248 } 249 return new Mac(algorithm); 250 } 251 throw new NoSuchAlgorithmException 252 ("Algorithm " + algorithm + " not available"); 253 } 254 255 /** 256 * Returns a <code>Mac</code> object that implements the 257 * specified MAC algorithm. 258 * 259 * <p> A new Mac object encapsulating the 260 * MacSpi implementation from the specified provider 261 * is returned. The specified provider must be registered 262 * in the security provider list. 263 * 264 * <p> Note that the list of registered providers may be retrieved via 265 * the {@link Security#getProviders() Security.getProviders()} method. 266 * 267 * @param algorithm the standard name of the requested MAC algorithm. 268 * See the Mac section in the <a href= 269 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 270 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 271 * for information about standard algorithm names. 272 * 273 * @param provider the name of the provider. 274 * 275 * @return the new <code>Mac</code> object. 276 * 277 * @exception NoSuchAlgorithmException if a MacSpi 278 * implementation for the specified algorithm is not 279 * available from the specified provider. 280 * 281 * @exception NoSuchProviderException if the specified provider is not 282 * registered in the security provider list. 283 * 284 * @exception IllegalArgumentException if the <code>provider</code> 285 * is null or empty. 286 * 287 * @see java.security.Provider 288 */ getInstance(String algorithm, String provider)289 public static final Mac getInstance(String algorithm, String provider) 290 throws NoSuchAlgorithmException, NoSuchProviderException { 291 Instance instance = JceSecurity.getInstance 292 ("Mac", MacSpi.class, algorithm, provider); 293 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 294 } 295 296 /** 297 * Returns a <code>Mac</code> object that implements the 298 * specified MAC algorithm. 299 * 300 * <p> A new Mac object encapsulating the 301 * MacSpi implementation from the specified Provider 302 * object is returned. Note that the specified Provider object 303 * does not have to be registered in the provider list. 304 * 305 * @param algorithm the standard name of the requested MAC algorithm. 306 * See the Mac section in the <a href= 307 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 308 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 309 * for information about standard algorithm names. 310 * 311 * @param provider the provider. 312 * 313 * @return the new <code>Mac</code> object. 314 * 315 * @exception NoSuchAlgorithmException if a MacSpi 316 * implementation for the specified algorithm is not available 317 * from the specified Provider object. 318 * 319 * @exception IllegalArgumentException if the <code>provider</code> 320 * is null. 321 * 322 * @see java.security.Provider 323 */ getInstance(String algorithm, Provider provider)324 public static final Mac getInstance(String algorithm, Provider provider) 325 throws NoSuchAlgorithmException { 326 Instance instance = JceSecurity.getInstance 327 ("Mac", MacSpi.class, algorithm, provider); 328 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 329 } 330 331 // max number of debug warnings to print from chooseFirstProvider() 332 private static int warnCount = 10; 333 334 /** 335 * Choose the Spi from the first provider available. Used if 336 * delayed provider selection is not possible because init() 337 * is not the first method called. 338 */ chooseFirstProvider()339 void chooseFirstProvider() { 340 if (spi != null || lock == null) { 341 return; 342 } 343 synchronized (lock) { 344 if (spi != null) { 345 return; 346 } 347 /* Android-removed: this debugging mechanism is not used in Android. 348 if (debug != null) { 349 int w = --warnCount; 350 if (w >= 0) { 351 debug.println("Mac.init() not first method " 352 + "called, disabling delayed provider selection"); 353 if (w == 0) { 354 debug.println("Further warnings of this type will " 355 + "be suppressed"); 356 } 357 new Exception("Call trace").printStackTrace(); 358 } 359 } 360 */ 361 Exception lastException = null; 362 for (Service s : GetInstance.getServices("Mac", algorithm)) { 363 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 364 continue; 365 } 366 try { 367 Object obj = s.newInstance(null); 368 if (obj instanceof MacSpi == false) { 369 continue; 370 } 371 spi = (MacSpi)obj; 372 provider = s.getProvider(); 373 return; 374 } catch (NoSuchAlgorithmException e) { 375 lastException = e; 376 } 377 } 378 ProviderException e = new ProviderException 379 ("Could not construct MacSpi instance"); 380 if (lastException != null) { 381 e.initCause(lastException); 382 } 383 throw e; 384 } 385 } 386 chooseProvider(Key key, AlgorithmParameterSpec params)387 private void chooseProvider(Key key, AlgorithmParameterSpec params) 388 throws InvalidKeyException, InvalidAlgorithmParameterException { 389 synchronized (lock) { 390 if (spi != null && (key == null || lock == null)) { 391 spi.engineInit(key, params); 392 return; 393 } 394 Exception lastException = null; 395 for (Service s : GetInstance.getServices("Mac", algorithm)) { 396 // if provider says it does not support this key, ignore it 397 if (s.supportsParameter(key) == false) { 398 continue; 399 } 400 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 401 continue; 402 } 403 try { 404 MacSpi spi = (MacSpi)s.newInstance(null); 405 spi.engineInit(key, params); 406 provider = s.getProvider(); 407 this.spi = spi; 408 return; 409 } catch (Exception e) { 410 // NoSuchAlgorithmException from newInstance() 411 // InvalidKeyException from init() 412 // RuntimeException (ProviderException) from init() 413 if (lastException == null) { 414 lastException = e; 415 } 416 } 417 } 418 // no working provider found, fail 419 if (lastException instanceof InvalidKeyException) { 420 throw (InvalidKeyException)lastException; 421 } 422 if (lastException instanceof InvalidAlgorithmParameterException) { 423 throw (InvalidAlgorithmParameterException)lastException; 424 } 425 if (lastException instanceof RuntimeException) { 426 throw (RuntimeException)lastException; 427 } 428 String kName = (key != null) ? key.getClass().getName() : "(null)"; 429 throw new InvalidKeyException 430 ("No installed provider supports this key: " 431 + kName, lastException); 432 } 433 } 434 435 /** 436 * Returns the provider of this <code>Mac</code> object. 437 * 438 * @return the provider of this <code>Mac</code> object. 439 */ getProvider()440 public final Provider getProvider() { 441 chooseFirstProvider(); 442 return this.provider; 443 } 444 445 /** 446 * Returns the length of the MAC in bytes. 447 * 448 * @return the MAC length in bytes. 449 */ getMacLength()450 public final int getMacLength() { 451 chooseFirstProvider(); 452 return spi.engineGetMacLength(); 453 } 454 455 /** 456 * Initializes this <code>Mac</code> object with the given key. 457 * 458 * @param key the key. 459 * 460 * @exception InvalidKeyException if the given key is inappropriate for 461 * initializing this MAC. 462 */ init(Key key)463 public final void init(Key key) throws InvalidKeyException { 464 try { 465 if (spi != null && (key == null || lock == null)) { 466 spi.engineInit(key, null); 467 } else { 468 chooseProvider(key, null); 469 } 470 } catch (InvalidAlgorithmParameterException e) { 471 throw new InvalidKeyException("init() failed", e); 472 } 473 initialized = true; 474 475 /* Android-removed: this debugging mechanism is not used in Android. 476 if (!skipDebug && pdebug != null) { 477 pdebug.println("Mac." + algorithm + " algorithm from: " + 478 this.provider.getName()); 479 } 480 */ 481 } 482 483 /** 484 * Initializes this <code>Mac</code> object with the given key and 485 * algorithm parameters. 486 * 487 * @param key the key. 488 * @param params the algorithm parameters. 489 * 490 * @exception InvalidKeyException if the given key is inappropriate for 491 * initializing this MAC. 492 * @exception InvalidAlgorithmParameterException if the given algorithm 493 * parameters are inappropriate for this MAC. 494 */ init(Key key, AlgorithmParameterSpec params)495 public final void init(Key key, AlgorithmParameterSpec params) 496 throws InvalidKeyException, InvalidAlgorithmParameterException { 497 if (spi != null && (key == null || lock == null)) { 498 spi.engineInit(key, params); 499 } else { 500 chooseProvider(key, params); 501 } 502 initialized = true; 503 504 /* Android-removed: this debugging mechanism is not used in Android. 505 if (!skipDebug && pdebug != null) { 506 pdebug.println("Mac." + algorithm + " algorithm from: " + 507 this.provider.getName()); 508 } 509 */ 510 } 511 512 /** 513 * Processes the given byte. 514 * 515 * @param input the input byte to be processed. 516 * 517 * @exception IllegalStateException if this <code>Mac</code> has not been 518 * initialized. 519 */ update(byte input)520 public final void update(byte input) throws IllegalStateException { 521 chooseFirstProvider(); 522 if (initialized == false) { 523 throw new IllegalStateException("MAC not initialized"); 524 } 525 spi.engineUpdate(input); 526 } 527 528 /** 529 * Processes the given array of bytes. 530 * 531 * @param input the array of bytes to be processed. 532 * 533 * @exception IllegalStateException if this <code>Mac</code> has not been 534 * initialized. 535 */ update(byte[] input)536 public final void update(byte[] input) throws IllegalStateException { 537 chooseFirstProvider(); 538 if (initialized == false) { 539 throw new IllegalStateException("MAC not initialized"); 540 } 541 if (input != null) { 542 spi.engineUpdate(input, 0, input.length); 543 } 544 } 545 546 /** 547 * Processes the first <code>len</code> bytes in <code>input</code>, 548 * starting at <code>offset</code> inclusive. 549 * 550 * @param input the input buffer. 551 * @param offset the offset in <code>input</code> where the input starts. 552 * @param len the number of bytes to process. 553 * 554 * @exception IllegalStateException if this <code>Mac</code> has not been 555 * initialized. 556 */ update(byte[] input, int offset, int len)557 public final void update(byte[] input, int offset, int len) 558 throws IllegalStateException { 559 chooseFirstProvider(); 560 if (initialized == false) { 561 throw new IllegalStateException("MAC not initialized"); 562 } 563 564 if (input != null) { 565 if ((offset < 0) || (len > (input.length - offset)) || (len < 0)) 566 throw new IllegalArgumentException("Bad arguments"); 567 spi.engineUpdate(input, offset, len); 568 } 569 } 570 571 /** 572 * Processes <code>input.remaining()</code> bytes in the ByteBuffer 573 * <code>input</code>, starting at <code>input.position()</code>. 574 * Upon return, the buffer's position will be equal to its limit; 575 * its limit will not have changed. 576 * 577 * @param input the ByteBuffer 578 * 579 * @exception IllegalStateException if this <code>Mac</code> has not been 580 * initialized. 581 * @since 1.5 582 */ update(ByteBuffer input)583 public final void update(ByteBuffer input) { 584 chooseFirstProvider(); 585 if (initialized == false) { 586 throw new IllegalStateException("MAC not initialized"); 587 } 588 if (input == null) { 589 throw new IllegalArgumentException("Buffer must not be null"); 590 } 591 spi.engineUpdate(input); 592 } 593 594 /** 595 * Finishes the MAC operation. 596 * 597 * <p>A call to this method resets this <code>Mac</code> object to the 598 * state it was in when previously initialized via a call to 599 * <code>init(Key)</code> or 600 * <code>init(Key, AlgorithmParameterSpec)</code>. 601 * That is, the object is reset and available to generate another MAC from 602 * the same key, if desired, via new calls to <code>update</code> and 603 * <code>doFinal</code>. 604 * (In order to reuse this <code>Mac</code> object with a different key, 605 * it must be reinitialized via a call to <code>init(Key)</code> or 606 * <code>init(Key, AlgorithmParameterSpec)</code>. 607 * 608 * @return the MAC result. 609 * 610 * @exception IllegalStateException if this <code>Mac</code> has not been 611 * initialized. 612 */ doFinal()613 public final byte[] doFinal() throws IllegalStateException { 614 chooseFirstProvider(); 615 if (initialized == false) { 616 throw new IllegalStateException("MAC not initialized"); 617 } 618 byte[] mac = spi.engineDoFinal(); 619 spi.engineReset(); 620 return mac; 621 } 622 623 /** 624 * Finishes the MAC operation. 625 * 626 * <p>A call to this method resets this <code>Mac</code> object to the 627 * state it was in when previously initialized via a call to 628 * <code>init(Key)</code> or 629 * <code>init(Key, AlgorithmParameterSpec)</code>. 630 * That is, the object is reset and available to generate another MAC from 631 * the same key, if desired, via new calls to <code>update</code> and 632 * <code>doFinal</code>. 633 * (In order to reuse this <code>Mac</code> object with a different key, 634 * it must be reinitialized via a call to <code>init(Key)</code> or 635 * <code>init(Key, AlgorithmParameterSpec)</code>. 636 * 637 * <p>The MAC result is stored in <code>output</code>, starting at 638 * <code>outOffset</code> inclusive. 639 * 640 * @param output the buffer where the MAC result is stored 641 * @param outOffset the offset in <code>output</code> where the MAC is 642 * stored 643 * 644 * @exception ShortBufferException if the given output buffer is too small 645 * to hold the result 646 * @exception IllegalStateException if this <code>Mac</code> has not been 647 * initialized. 648 */ doFinal(byte[] output, int outOffset)649 public final void doFinal(byte[] output, int outOffset) 650 throws ShortBufferException, IllegalStateException 651 { 652 chooseFirstProvider(); 653 if (initialized == false) { 654 throw new IllegalStateException("MAC not initialized"); 655 } 656 int macLen = getMacLength(); 657 if (output == null || output.length-outOffset < macLen) { 658 throw new ShortBufferException 659 ("Cannot store MAC in output buffer"); 660 } 661 byte[] mac = doFinal(); 662 System.arraycopy(mac, 0, output, outOffset, macLen); 663 return; 664 } 665 666 /** 667 * Processes the given array of bytes and finishes the MAC operation. 668 * 669 * <p>A call to this method resets this <code>Mac</code> object to the 670 * state it was in when previously initialized via a call to 671 * <code>init(Key)</code> or 672 * <code>init(Key, AlgorithmParameterSpec)</code>. 673 * That is, the object is reset and available to generate another MAC from 674 * the same key, if desired, via new calls to <code>update</code> and 675 * <code>doFinal</code>. 676 * (In order to reuse this <code>Mac</code> object with a different key, 677 * it must be reinitialized via a call to <code>init(Key)</code> or 678 * <code>init(Key, AlgorithmParameterSpec)</code>. 679 * 680 * @param input data in bytes 681 * @return the MAC result. 682 * 683 * @exception IllegalStateException if this <code>Mac</code> has not been 684 * initialized. 685 */ doFinal(byte[] input)686 public final byte[] doFinal(byte[] input) throws IllegalStateException 687 { 688 chooseFirstProvider(); 689 if (initialized == false) { 690 throw new IllegalStateException("MAC not initialized"); 691 } 692 update(input); 693 return doFinal(); 694 } 695 696 /** 697 * Resets this <code>Mac</code> object. 698 * 699 * <p>A call to this method resets this <code>Mac</code> object to the 700 * state it was in when previously initialized via a call to 701 * <code>init(Key)</code> or 702 * <code>init(Key, AlgorithmParameterSpec)</code>. 703 * That is, the object is reset and available to generate another MAC from 704 * the same key, if desired, via new calls to <code>update</code> and 705 * <code>doFinal</code>. 706 * (In order to reuse this <code>Mac</code> object with a different key, 707 * it must be reinitialized via a call to <code>init(Key)</code> or 708 * <code>init(Key, AlgorithmParameterSpec)</code>. 709 */ reset()710 public final void reset() { 711 chooseFirstProvider(); 712 spi.engineReset(); 713 } 714 715 /** 716 * Returns a clone if the provider implementation is cloneable. 717 * 718 * @return a clone if the provider implementation is cloneable. 719 * 720 * @exception CloneNotSupportedException if this is called on a 721 * delegate that does not support <code>Cloneable</code>. 722 */ clone()723 public final Object clone() throws CloneNotSupportedException { 724 chooseFirstProvider(); 725 Mac that = (Mac)super.clone(); 726 that.spi = (MacSpi)this.spi.clone(); 727 return that; 728 } 729 730 /** 731 * Returns the {@code MacSpi} backing this {@code Mac} or {@code null} if no {@code MacSpi} is 732 * backing this {@code Mac}. 733 * 734 * @hide 735 */ getCurrentSpi()736 public MacSpi getCurrentSpi() { 737 return spi; 738 } 739 } 740